mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-13 17:22:15 -05:00
Enabling FILEGROUPS tab experience to the database properties (#24226)
* initial changes for loadin dsc table with real values from smo * Displaying diff columns for DSC for diff sql server * checkbox maiants the selection * elevate option fails to load correct value when set to when_supported option * all working till maxdop, todo pause option, save * commented MAXDOP changes, as it is causing issues * primary,sec,checkbox working as expected, TODO:MaxDop etc options,saving,tests * Undo MAXDOP commented code * refactored with service data * column header width adjustments * Maxdop and pause resume options completed, apply button is failing now * Removed option names from loc and using Id instead as names may change in future like in doc * Apply button fixed * refactored to reduce table reload * Ledger digest completed * refactor done: maxdop secondary shows wrong data from pause_resume * refactor more: all working but table focus disturbs on update table * adds conditions for unsupported dsc to <2016 server * maxdop secondary checkbox fix * rows still loses focus after value change due to update table row data * Fixed updating secondary dropdown value * reusing the private method and removed the duplicated codes * initial commit - fullText and owner need revision * Enter key in input type allows the change to update the table data, reduces the live update issues * Setting focus to the current row * loading data, need stylings-increase col length, etc * using the existed setTableData method * Adding new file dialog * creating addFile, but not displaying in table, issue with appendData * Adding row to the table, options are getting from STS * all working except InPercent value * code review comment updates * Input type checkbox update table additional validation * all except path * fixing the input type focus and reverting the enterKeyPress logic * browse path is created, need stylings,refactor,filestream selection and add * fixing the flickering issue with data refresh * new file options toggle and grid display string updates * moving code inline and using actual component * cleanup * Add file saving is done, except one styling issue with autogrowth section * add,remove working, need to edit file * add, edit, remove - all working, need css fixes and -1 fix * addressing code review comments * adding local changes adn fixing for edit file * adjusting css * addressing code review comment for using loc var instead of duplicated line of code to get the rowinfo * all fixed, need testing and refactor * vBump STS and fixing required field causing the apply button not enable for other options on main branch * fixing autogrowth radio buttons change updates * all working except some css * disabled size for filestream * fixing filegroups and filetypes scnearios, added filename validation for newfile, todo:editingNew file * added max and min values to the inputs * editing filename validation completed, all done exccept CSS * all fixed except scroll bar * edit db file header, filename enable issue fix * PR comment supporting updates for STS * initial commit * min updates * loading data into table.. adding rows * modfying addButtonsForTable method and reusing it for edit button * add empty row/remove done, edit name and other columns required and save with tests * All working except new name validation * adding validation * code review comment updates * Dialogbase addbuttons to table refactored * more typo fixes * all working except 'Remove' revist logic and delete correct row * removing fulltext index prop * adding defualt conditions to the columns checkboxes * service fix * using path.join instead of hardcoded separators * updating files is updating filegroups tabs, removing fg to file update required * fixed toggle remove button for tab;es * filegroup refactor * update filegroups and files on new fg name * final commit changes * code review updates * vBump STS to 4.9.0.26
This commit is contained in:
committed by
GitHub
parent
e4abe4d167
commit
41202a65bd
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
||||
"version": "4.9.0.25",
|
||||
"version": "4.9.0.26",
|
||||
"downloadFileNames": {
|
||||
"Windows_86": "win-x86-net7.0.zip",
|
||||
"Windows_64": "win-x64-net7.0.zip",
|
||||
|
||||
@@ -39,6 +39,7 @@ export const DatabaseOptionsPropertiesDocUrl = 'https://learn.microsoft.com/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 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 enum TelemetryActions {
|
||||
CreateObject = 'CreateObject',
|
||||
|
||||
@@ -457,6 +457,7 @@ export interface Database extends ObjectManagement.SqlObject {
|
||||
databaseScopedConfigurations: DatabaseScopedConfigurationsInfo[];
|
||||
isFilesTabSupported?: boolean;
|
||||
files?: DatabaseFile[];
|
||||
filegroups?: FileGroup[];
|
||||
}
|
||||
|
||||
export interface DatabaseViewInfo extends ObjectManagement.ObjectViewInfo<Database> {
|
||||
@@ -575,6 +576,12 @@ export const enum FileGrowthType {
|
||||
None = 99
|
||||
}
|
||||
|
||||
export const enum FileGroupType {
|
||||
RowsFileGroup = 0,
|
||||
FileStreamDataFileGroup = 2,
|
||||
MemoryOptimizedDataFileGroup = 3
|
||||
}
|
||||
|
||||
export interface DatabaseFile {
|
||||
id: number;
|
||||
name: string;
|
||||
@@ -588,3 +595,12 @@ export interface DatabaseFile {
|
||||
autoFileGrowthType: FileGrowthType;
|
||||
maxSizeLimitInMb: number
|
||||
}
|
||||
|
||||
export interface FileGroup {
|
||||
id?: number;
|
||||
name: string;
|
||||
type: FileGroupType;
|
||||
isReadOnly: boolean;
|
||||
isDefault: boolean;
|
||||
autogrowAllFiles: boolean;
|
||||
}
|
||||
|
||||
@@ -167,6 +167,7 @@ export const GeneralSectionHeader = localize('objectManagement.generalSectionHea
|
||||
export const AdvancedSectionHeader = localize('objectManagement.advancedSectionHeader', "Advanced");
|
||||
export const OptionsSectionHeader = localize('objectManagement.optionsSectionHeader', "Options");
|
||||
export const FilesSectionHeader = localize('objectManagement.optionsSectionHeader', "Files");
|
||||
export const FileGroupsSectionHeader = localize('objectManagement.filegroupsSectionHeader', "Filegroups");
|
||||
export const PasswordText = localize('objectManagement.passwordLabel', "Password");
|
||||
export const ConfirmPasswordText = localize('objectManagement.confirmPasswordLabel', "Confirm password");
|
||||
export const EnabledText = localize('objectManagement.enabledLabel', "Enabled");
|
||||
@@ -394,6 +395,17 @@ export const FilegrowthLimitError = localize('objectManagement.databasePropertie
|
||||
export const RowsDataFileType = localize('objectManagement.databaseProperties.rowsDataFileType', "ROWS Data");
|
||||
export const LogFiletype = localize('objectManagement.databaseProperties.logfiletype', "LOG");
|
||||
export const FilestreamFileType = localize('objectManagement.databaseProperties.filestreamFileType', "FILESTREAM Data");
|
||||
export const RowsFileGroupsSectionText = localize('objectManagement.databaseProperties.rowsFileGroupsSectionText', "Rows");
|
||||
export const FileStreamFileGroupsSectionText = localize('objectManagement.databaseProperties.fileStreamFileGroupsSectionText', "FileStream");
|
||||
export const MemoryOptimizedFileGroupsSectionText = localize('objectManagement.databaseProperties.memoryOptimizedFileGroupsSectionText', "Memory Optimized Data");
|
||||
export const FilesText = localize('objectManagement.databaseProperties.filesText', "Files");
|
||||
export const ReadOnlyText = localize('objectManagement.databaseProperties.readOnlyText', "Read-Only");
|
||||
export const DefaultText = localize('objectManagement.databaseProperties.defaultText', "Default");
|
||||
export const AutogrowAllFilesText = localize('objectManagement.databaseProperties.autogrowAllFilesText', "Autogrow All Files");
|
||||
export const FilestreamFilesText = localize('objectManagement.databaseProperties.filestreamFilesText', "Filestream Files");
|
||||
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 EmptyFilegroupNameError = localize('objectManagement.databaseProperties.emptyFilegroupNameError', "Cannot use empty object names for filegroups.");
|
||||
|
||||
// Util functions
|
||||
export function getNodeTypeDisplayName(type: string, inTitle: boolean = false): string {
|
||||
|
||||
@@ -8,8 +8,8 @@ import { ObjectManagementDialogBase, ObjectManagementDialogOptions } from './obj
|
||||
import { DefaultInputWidth, DefaultTableWidth, DefaultMinTableRowCount, DefaultMaxTableRowCount, getTableHeight, DialogButton } from '../../ui/dialogBase';
|
||||
import { IObjectManagementService } from 'mssql';
|
||||
import * as localizedConstants from '../localizedConstants';
|
||||
import { CreateDatabaseDocUrl, DatabaseGeneralPropertiesDocUrl, DatabaseFilesPropertiesDocUrl, DatabaseOptionsPropertiesDocUrl, DatabaseScopedConfigurationPropertiesDocUrl } from '../constants';
|
||||
import { Database, DatabaseFile, DatabaseScopedConfigurationsInfo, DatabaseViewInfo, FileGrowthType } from '../interfaces';
|
||||
import { CreateDatabaseDocUrl, DatabaseGeneralPropertiesDocUrl, DatabaseFilesPropertiesDocUrl, DatabaseOptionsPropertiesDocUrl, DatabaseScopedConfigurationPropertiesDocUrl, DatabaseFileGroupsPropertiesDocUrl } from '../constants';
|
||||
import { Database, DatabaseFile, DatabaseScopedConfigurationsInfo, DatabaseViewInfo, FileGrowthType, FileGroup, FileGroupType } from '../interfaces';
|
||||
import { convertNumToTwoDecimalStringInMB } from '../utils';
|
||||
import { isUndefinedOrNull } from '../../types';
|
||||
import { deepClone } from '../../util/objects';
|
||||
@@ -24,6 +24,7 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
||||
private generalTab: azdata.Tab;
|
||||
private filesTab: azdata.Tab;
|
||||
private optionsTab: azdata.Tab;
|
||||
private fileGroupsTab: azdata.Tab;
|
||||
private dscTab: azdata.Tab;
|
||||
private optionsTabSectionsContainer: azdata.Component[] = [];
|
||||
private activeTabId: string;
|
||||
@@ -48,6 +49,20 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
||||
// Files Tab
|
||||
private readonly filesTabId: string = 'filesDatabaseId';
|
||||
private databaseFilesTable: azdata.TableComponent;
|
||||
private rowDatafileGroupsOptions: string[];
|
||||
private filestreamDatafileGroupsOptions: string[];
|
||||
// fileGroups Tab
|
||||
private readonly fileGroupsTabId: string = 'fileGroupsDatabaseId';
|
||||
private rowsFilegroupsTable: azdata.TableComponent;
|
||||
private filestreamFilegroupsTable: azdata.TableComponent;
|
||||
private memoryOptimizedFilegroupsTable: azdata.TableComponent;
|
||||
private rowsFilegroupNameInput: azdata.InputBoxComponent;
|
||||
private filestreamFilegroupNameInput: azdata.InputBoxComponent;
|
||||
private memoryOptimizedFilegroupNameInput: azdata.InputBoxComponent;
|
||||
private newFileGroupTemporaryId: number = 0;
|
||||
private rowDataFileGroupsTableRows: FileGroup[] = [];
|
||||
private filestreamDataFileGroupsTableRows: FileGroup[] = [];
|
||||
private memoryoptimizedFileGroupsTableRows: FileGroup[] = [];
|
||||
// Options Tab
|
||||
private readonly optionsTabId: string = 'optionsDatabaseId';
|
||||
private autoCreateIncrementalStatisticsInput: azdata.CheckBoxComponent;
|
||||
@@ -99,6 +114,9 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
||||
case this.filesTabId:
|
||||
helpUrl = DatabaseFilesPropertiesDocUrl;
|
||||
break;
|
||||
case this.fileGroupsTabId:
|
||||
helpUrl = DatabaseFileGroupsPropertiesDocUrl;
|
||||
break;
|
||||
case this.optionsTabId:
|
||||
helpUrl = DatabaseOptionsPropertiesDocUrl;
|
||||
break;
|
||||
@@ -149,6 +167,11 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
||||
};
|
||||
tabs.push(this.generalTab);
|
||||
|
||||
// Prepare the copies of individual filegroups tables data and filegroups options for files tab
|
||||
if (!isUndefinedOrNull(this.objectInfo.filegroups)) {
|
||||
this.updateFileGroupsOptionsAndTableRows();
|
||||
}
|
||||
|
||||
// Initialize Files Tab
|
||||
// Files tab is only enabled for SQL Server properties view
|
||||
if (!isUndefinedOrNull(this.objectInfo.isFilesTabSupported)) {
|
||||
@@ -162,6 +185,19 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
||||
tabs.push(this.filesTab);
|
||||
}
|
||||
|
||||
// Initilaize FileGroups Tab
|
||||
if (!isUndefinedOrNull(this.objectInfo.filegroups)) {
|
||||
const rowsFileGroupSection = await this.initializeRowsFileGroupSection();
|
||||
const fileStreamFileGroupSection = this.initializeFileStreamFileGroupSection();
|
||||
const memoryOptimizedFileGroupSection = this.initializeMemoryOptimizedFileGroupSection();
|
||||
this.fileGroupsTab = {
|
||||
title: localizedConstants.FileGroupsSectionHeader,
|
||||
id: this.fileGroupsTabId,
|
||||
content: this.createGroup('', [rowsFileGroupSection, fileStreamFileGroupSection, memoryOptimizedFileGroupSection], false)
|
||||
};
|
||||
tabs.push(this.fileGroupsTab);
|
||||
}
|
||||
|
||||
// Initialize Options Tab
|
||||
this.optionsTab = {
|
||||
title: localizedConstants.OptionsSectionHeader,
|
||||
@@ -207,6 +243,20 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
||||
if (collationNames?.length > 0 && !collationNames.some(name => name.toLowerCase() === this.objectInfo.collationName?.toLowerCase())) {
|
||||
errors.push(localizedConstants.CollationNotValidError(this.objectInfo.collationName ?? ''));
|
||||
}
|
||||
|
||||
// Validate Rows Filegroup names
|
||||
if (this.objectInfo.filegroups?.length > 0) {
|
||||
let seenFilegroups = new Set<string>;
|
||||
this.objectInfo.filegroups.map(function (item) {
|
||||
if (item.name === '') {
|
||||
errors.push(localizedConstants.EmptyFilegroupNameError);
|
||||
} else if (seenFilegroups.has(item.name)) {
|
||||
errors.push(localizedConstants.FilegroupExistsError(item.name));
|
||||
} else {
|
||||
seenFilegroups.add(item.name)
|
||||
}
|
||||
});
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
@@ -527,7 +577,8 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
||||
var newData = this.objectInfo.files?.map(file => {
|
||||
return this.convertToDataView(file);
|
||||
});
|
||||
await this.setTableData(this.databaseFilesTable, newData, DefaultMaxTableRowCount)
|
||||
await this.setTableData(this.databaseFilesTable, newData, DefaultMaxTableRowCount);
|
||||
await this.updateFileGroupsTablesfileCount(result.type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -539,7 +590,8 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
||||
var newData = this.objectInfo.files?.map(file => {
|
||||
return this.convertToDataView(file);
|
||||
});
|
||||
await this.setTableData(this.databaseFilesTable, newData, DefaultMaxTableRowCount)
|
||||
await this.setTableData(this.databaseFilesTable, newData, DefaultMaxTableRowCount);
|
||||
await this.updateFileGroupsTablesfileCount(result.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -549,11 +601,29 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
||||
*/
|
||||
private async onRemoveDatabaseFilesButtonClicked(): Promise<void> {
|
||||
if (this.databaseFilesTable.selectedRows.length === 1) {
|
||||
await this.updateFileGroupsTablesfileCount(this.objectInfo.files[this.databaseFilesTable.selectedRows[0]].type);
|
||||
this.objectInfo.files?.splice(this.databaseFilesTable.selectedRows[0], 1);
|
||||
var newData = this.objectInfo.files?.map(file => {
|
||||
return this.convertToDataView(file);
|
||||
});
|
||||
await this.setTableData(this.databaseFilesTable, newData, DefaultMaxTableRowCount)
|
||||
await this.setTableData(this.databaseFilesTable, newData, DefaultMaxTableRowCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updating the filegroups tables number of files count for each action of adding/editing/removing a database file
|
||||
* @param fileType type of the file to get the data for the table
|
||||
*/
|
||||
private async updateFileGroupsTablesfileCount(fileType: string): Promise<void> {
|
||||
if (fileType === localizedConstants.RowsDataFileType) {
|
||||
let data = this.getTableData(FileGroupType.RowsFileGroup);
|
||||
await this.setTableData(this.rowsFilegroupsTable, data);
|
||||
}
|
||||
else if (fileType === localizedConstants.FilestreamFileType) {
|
||||
let data = this.getTableData(FileGroupType.FileStreamDataFileGroup);
|
||||
await this.setTableData(this.filestreamFilegroupsTable, data);
|
||||
data = this.getTableData(FileGroupType.MemoryOptimizedDataFileGroup);
|
||||
await this.setTableData(this.memoryOptimizedFilegroupsTable, data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -561,9 +631,9 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
||||
* Validate the selected row to enable/disable the remove button
|
||||
* @returns true if the remove button should be enabled, false otherwise
|
||||
*/
|
||||
protected override get removeButtonEnabled(): boolean {
|
||||
protected override removeButtonEnabled(table: azdata.TableComponent): boolean {
|
||||
let isEnabled = true;
|
||||
if (this.databaseFilesTable.selectedRows !== undefined) {
|
||||
if (table === this.databaseFilesTable && this.databaseFilesTable.selectedRows !== undefined) {
|
||||
const selectedRowId = this.objectInfo.files[this.databaseFilesTable.selectedRows[0]].id;
|
||||
// Cannot delete a Primary row data file, Id is always 1.
|
||||
if (this.databaseFilesTable.selectedRows.length === 1 && selectedRowId === 1) {
|
||||
@@ -579,6 +649,13 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (table === this.rowsFilegroupsTable && this.rowsFilegroupsTable.selectedRows !== undefined && this.rowsFilegroupsTable.selectedRows.length === 1) {
|
||||
const selectedRow = this.rowDataFileGroupsTableRows[this.rowsFilegroupsTable.selectedRows[0]];
|
||||
// Cannot delete a row file if the fileGroup is Primary.
|
||||
if (selectedRow.name === 'PRIMARY' && selectedRow.id > 0) {
|
||||
isEnabled = false;
|
||||
}
|
||||
}
|
||||
return isEnabled;
|
||||
}
|
||||
|
||||
@@ -598,7 +675,7 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
||||
name: '',
|
||||
type: localizedConstants.RowsDataFileType,
|
||||
path: this.objectInfo.files[0].path,
|
||||
fileGroup: this.viewInfo.rowDataFileGroupsOptions[0],
|
||||
fileGroup: this.rowDatafileGroupsOptions.find(option => option === 'PRIMARY'),
|
||||
fileNameWithExtension: '',
|
||||
sizeInMb: defaultFileSizeInMb,
|
||||
isAutoGrowthEnabled: true,
|
||||
@@ -611,6 +688,8 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
||||
title: (isNewFile || isEditingNewFile) ? localizedConstants.AddDatabaseFilesText : localizedConstants.EditDatabaseFilesText(databaseFile.name),
|
||||
viewInfo: this.viewInfo,
|
||||
files: this.objectInfo.files,
|
||||
rowFilegroups: this.rowDatafileGroupsOptions,
|
||||
filestreamFilegroups: this.filestreamDatafileGroupsOptions,
|
||||
isNewFile: isNewFile,
|
||||
isEditingNewFile: isEditingNewFile,
|
||||
databaseFile: databaseFile,
|
||||
@@ -627,6 +706,402 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Database Properties - FileGroups Tab
|
||||
/**
|
||||
* Initializes the rows filegroups section and updates the table data
|
||||
* @returns Row data filegroups container
|
||||
*/
|
||||
private async initializeRowsFileGroupSection(): Promise<azdata.GroupContainer> {
|
||||
const data = this.getTableData(FileGroupType.RowsFileGroup);
|
||||
this.rowsFilegroupsTable = this.modelView.modelBuilder.table().withProps({
|
||||
columns: [{
|
||||
type: azdata.ColumnType.text,
|
||||
value: localizedConstants.NameText,
|
||||
width: 120
|
||||
}, {
|
||||
type: azdata.ColumnType.text,
|
||||
value: localizedConstants.FilesText,
|
||||
width: 60
|
||||
}, {
|
||||
type: azdata.ColumnType.checkBox,
|
||||
value: localizedConstants.ReadOnlyText,
|
||||
width: 80
|
||||
}, {
|
||||
type: azdata.ColumnType.checkBox,
|
||||
value: localizedConstants.DefaultText,
|
||||
width: 80
|
||||
}, {
|
||||
type: azdata.ColumnType.checkBox,
|
||||
value: localizedConstants.AutogrowAllFilesText,
|
||||
width: 110
|
||||
}],
|
||||
data: data,
|
||||
height: getTableHeight(data.length, DefaultMinTableRowCount, DefaultMaxTableRowCount),
|
||||
width: DefaultTableWidth,
|
||||
forceFitColumns: azdata.ColumnSizingMode.DataFit,
|
||||
CSSStyles: {
|
||||
'margin-left': '10px'
|
||||
}
|
||||
}).component();
|
||||
this.rowsFilegroupNameInput = this.getFilegroupNameInput(this.rowsFilegroupsTable, FileGroupType.RowsFileGroup);
|
||||
const addButtonComponent: DialogButton = {
|
||||
buttonAriaLabel: localizedConstants.AddFilegroupText,
|
||||
buttonHandler: () => this.onAddDatabaseFileGroupsButtonClicked(this.rowsFilegroupsTable)
|
||||
};
|
||||
const removeButtonComponent: DialogButton = {
|
||||
buttonAriaLabel: localizedConstants.RemoveButton,
|
||||
buttonHandler: () => this.onRemoveDatabaseFileGroupsButtonClicked(this.rowsFilegroupsTable)
|
||||
};
|
||||
const rowsFileGroupButtonContainer = this.addButtonsForTable(this.rowsFilegroupsTable, addButtonComponent, removeButtonComponent);
|
||||
|
||||
this.disposables.push(
|
||||
this.rowsFilegroupsTable.onCellAction(async (arg: azdata.ICheckboxCellActionEventArgs) => {
|
||||
let filegroup = this.rowDataFileGroupsTableRows[arg.row];
|
||||
// Read-Only column
|
||||
if (arg.column === 2) {
|
||||
filegroup.isReadOnly = arg.checked;
|
||||
}
|
||||
// Default column
|
||||
if (arg.column === 3) {
|
||||
this.updateFilegroupsDefaultColumnValues(arg, filegroup, FileGroupType.RowsFileGroup);
|
||||
}
|
||||
// Autogrow all files column
|
||||
if (arg.column === 4) {
|
||||
filegroup.autogrowAllFiles = arg.checked;
|
||||
}
|
||||
|
||||
// Refresh the table with updated data
|
||||
let data = this.getTableData(FileGroupType.RowsFileGroup);
|
||||
await this.setTableData(this.rowsFilegroupsTable, data);
|
||||
this.onFormFieldChange();
|
||||
}),
|
||||
this.rowsFilegroupsTable.onRowSelected(
|
||||
async () => {
|
||||
if (this.rowsFilegroupsTable.selectedRows.length === 1) {
|
||||
const fileGroup = this.rowDataFileGroupsTableRows[this.rowsFilegroupsTable.selectedRows[0]];
|
||||
await this.rowsFilegroupNameInput.updateCssStyles({ 'visibility': fileGroup.id < 0 ? 'visible' : 'hidden' });
|
||||
this.rowsFilegroupNameInput.value = fileGroup.name;
|
||||
this.onFormFieldChange();
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the filestream filegroups section and updates the table data
|
||||
* @returns filestream data filegroups container
|
||||
*/
|
||||
private initializeFileStreamFileGroupSection(): azdata.GroupContainer {
|
||||
const data = this.getTableData(FileGroupType.FileStreamDataFileGroup);
|
||||
this.filestreamFilegroupsTable = this.modelView.modelBuilder.table().withProps({
|
||||
columns: [{
|
||||
type: azdata.ColumnType.text,
|
||||
value: localizedConstants.NameText
|
||||
}, {
|
||||
type: azdata.ColumnType.text,
|
||||
value: localizedConstants.FilestreamFilesText
|
||||
}, {
|
||||
type: azdata.ColumnType.checkBox,
|
||||
value: localizedConstants.ReadOnlyText
|
||||
}, {
|
||||
type: azdata.ColumnType.checkBox,
|
||||
value: localizedConstants.DefaultText
|
||||
}],
|
||||
data: data,
|
||||
height: getTableHeight(data.length, DefaultMinTableRowCount, DefaultMaxTableRowCount),
|
||||
width: DefaultTableWidth,
|
||||
forceFitColumns: azdata.ColumnSizingMode.DataFit,
|
||||
CSSStyles: {
|
||||
'margin-left': '10px'
|
||||
}
|
||||
}).component();
|
||||
this.filestreamFilegroupNameInput = this.getFilegroupNameInput(this.filestreamFilegroupsTable, FileGroupType.FileStreamDataFileGroup);
|
||||
const addButtonComponent: DialogButton = {
|
||||
buttonAriaLabel: localizedConstants.AddFilegroupText,
|
||||
buttonHandler: () => this.onAddDatabaseFileGroupsButtonClicked(this.filestreamFilegroupsTable)
|
||||
};
|
||||
const removeButtonComponent: DialogButton = {
|
||||
buttonAriaLabel: localizedConstants.RemoveButton,
|
||||
buttonHandler: () => this.onRemoveDatabaseFileGroupsButtonClicked(this.filestreamFilegroupsTable)
|
||||
};
|
||||
const filestreamFileGroupButtonContainer = this.addButtonsForTable(this.filestreamFilegroupsTable, addButtonComponent, removeButtonComponent);
|
||||
|
||||
this.disposables.push(
|
||||
this.filestreamFilegroupsTable.onCellAction(async (arg: azdata.ICheckboxCellActionEventArgs) => {
|
||||
let filegroup = this.filestreamDataFileGroupsTableRows[arg.row];
|
||||
// Read-Only column
|
||||
if (arg.column === 2) {
|
||||
filegroup.isReadOnly = arg.checked;
|
||||
}
|
||||
// Default column
|
||||
else if (arg.column === 3) {
|
||||
this.updateFilegroupsDefaultColumnValues(arg, filegroup, FileGroupType.FileStreamDataFileGroup);
|
||||
}
|
||||
|
||||
// Refresh the table with updated data
|
||||
let data = this.getTableData(FileGroupType.FileStreamDataFileGroup);
|
||||
await this.setTableData(this.filestreamFilegroupsTable, data);
|
||||
this.onFormFieldChange();
|
||||
}),
|
||||
this.filestreamFilegroupsTable.onRowSelected(
|
||||
async () => {
|
||||
if (this.filestreamFilegroupsTable.selectedRows.length === 1) {
|
||||
const fileGroup = this.filestreamDataFileGroupsTableRows[this.filestreamFilegroupsTable.selectedRows[0]];
|
||||
await this.filestreamFilegroupNameInput.updateCssStyles({ 'visibility': fileGroup.id < 0 ? 'visible' : 'hidden' });
|
||||
this.filestreamFilegroupNameInput.value = fileGroup.name;
|
||||
this.onFormFieldChange();
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
const filestreamContainer = this.modelView.modelBuilder.flexContainer().withItems([this.filestreamFilegroupNameInput]).component();
|
||||
filestreamContainer.addItems([filestreamFileGroupButtonContainer], { flex: '0 0 auto' });
|
||||
return this.createGroup(localizedConstants.FileStreamFileGroupsSectionText, [this.filestreamFilegroupsTable, filestreamContainer], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the memory optimized filegroups section and updates the table data
|
||||
* @returns Memory optimized filegroups container
|
||||
*/
|
||||
private initializeMemoryOptimizedFileGroupSection(): azdata.GroupContainer {
|
||||
const data = this.getTableData(FileGroupType.MemoryOptimizedDataFileGroup);
|
||||
this.memoryOptimizedFilegroupsTable = this.modelView.modelBuilder.table().withProps({
|
||||
columns: [{
|
||||
type: azdata.ColumnType.text,
|
||||
value: localizedConstants.NameText
|
||||
}, {
|
||||
type: azdata.ColumnType.text,
|
||||
value: localizedConstants.FilestreamFilesText
|
||||
}],
|
||||
data: data,
|
||||
height: getTableHeight(data.length, DefaultMinTableRowCount, DefaultMaxTableRowCount),
|
||||
width: DefaultTableWidth,
|
||||
forceFitColumns: azdata.ColumnSizingMode.DataFit,
|
||||
CSSStyles: {
|
||||
'margin-left': '10px'
|
||||
}
|
||||
}).component();
|
||||
this.memoryOptimizedFilegroupNameInput = this.getFilegroupNameInput(this.memoryOptimizedFilegroupsTable, FileGroupType.MemoryOptimizedDataFileGroup);
|
||||
const addButtonComponent: DialogButton = {
|
||||
buttonAriaLabel: localizedConstants.AddFilegroupText,
|
||||
buttonHandler: () => this.onAddDatabaseFileGroupsButtonClicked(this.memoryOptimizedFilegroupsTable)
|
||||
};
|
||||
const removeButtonComponent: DialogButton = {
|
||||
buttonAriaLabel: localizedConstants.RemoveButton,
|
||||
buttonHandler: () => this.onRemoveDatabaseFileGroupsButtonClicked(this.memoryOptimizedFilegroupsTable)
|
||||
};
|
||||
const memoryOptimizedFileGroupButtonContainer = this.addButtonsForTable(this.memoryOptimizedFilegroupsTable, addButtonComponent, removeButtonComponent);
|
||||
|
||||
this.disposables.push(
|
||||
this.memoryOptimizedFilegroupsTable.onRowSelected(
|
||||
async () => {
|
||||
if (this.memoryOptimizedFilegroupsTable.selectedRows.length === 1) {
|
||||
const fileGroup = this.memoryoptimizedFileGroupsTableRows[this.memoryOptimizedFilegroupsTable.selectedRows[0]];
|
||||
await this.memoryOptimizedFilegroupNameInput.updateCssStyles({ 'visibility': fileGroup.id < 0 ? 'visible' : 'hidden' });
|
||||
this.memoryOptimizedFilegroupNameInput.value = fileGroup.name;
|
||||
this.onFormFieldChange();
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
const memoryOptimizedContainer = this.modelView.modelBuilder.flexContainer().withItems([this.memoryOptimizedFilegroupNameInput]).component();
|
||||
memoryOptimizedContainer.addItems([memoryOptimizedFileGroupButtonContainer], { flex: '0 0 auto' });
|
||||
return this.createGroup(localizedConstants.MemoryOptimizedFileGroupsSectionText, [this.memoryOptimizedFilegroupsTable, memoryOptimizedContainer], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the default value for the filegroup
|
||||
* @param arg selected checkbox event
|
||||
* @param filegroup filegroup object
|
||||
* @param filegroupType filegroup type
|
||||
*/
|
||||
private updateFilegroupsDefaultColumnValues(arg: azdata.ICheckboxCellActionEventArgs, filegroup: FileGroup, filegroupType: FileGroupType): void {
|
||||
if (arg.checked) {
|
||||
this.objectInfo.filegroups.forEach(fg => {
|
||||
if (fg.type === filegroupType) {
|
||||
fg.isDefault = fg.name === filegroup.name && fg.id === filegroup.id ? arg.checked : !arg.checked;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
filegroup.isDefault = arg.checked;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adding new row to the respective table on its add button click
|
||||
* @param table table component
|
||||
*/
|
||||
private async onAddDatabaseFileGroupsButtonClicked(table: azdata.TableComponent): 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);
|
||||
}
|
||||
else if (table === this.filestreamFilegroupsTable) {
|
||||
newRow.type = FileGroupType.FileStreamDataFileGroup;
|
||||
newRow.isReadOnly = false;
|
||||
newRow.isDefault = false;
|
||||
this.objectInfo.filegroups?.push(newRow);
|
||||
newData = this.getTableData(FileGroupType.FileStreamDataFileGroup);
|
||||
}
|
||||
else if (table === this.memoryOptimizedFilegroupsTable && this.memoryoptimizedFileGroupsTableRows.length < 1) {
|
||||
newRow.type = FileGroupType.MemoryOptimizedDataFileGroup;
|
||||
this.objectInfo.filegroups?.push(newRow);
|
||||
newData = this.getTableData(FileGroupType.MemoryOptimizedDataFileGroup);
|
||||
}
|
||||
|
||||
if (newData !== undefined) {
|
||||
// Refresh the table with new row data
|
||||
this.updateFileGroupsOptionsAndTableRows();
|
||||
await this.setTableData(table, newData, DefaultMaxTableRowCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
private updateFileGroupsOptionsAndTableRows(): void {
|
||||
// Filegroups rows for filegroups tab
|
||||
this.rowDataFileGroupsTableRows = this.objectInfo.filegroups?.filter(filegroup => filegroup.type === FileGroupType.RowsFileGroup);
|
||||
this.filestreamDataFileGroupsTableRows = this.objectInfo.filegroups?.filter(filegroup => filegroup.type === FileGroupType.FileStreamDataFileGroup);
|
||||
this.memoryoptimizedFileGroupsTableRows = this.objectInfo.filegroups?.filter(filegroup => filegroup.type === FileGroupType.MemoryOptimizedDataFileGroup);
|
||||
|
||||
// Filegroups options for files tab
|
||||
this.filestreamDatafileGroupsOptions = this.objectInfo.filegroups?.filter(filegroup => filegroup.type === FileGroupType.FileStreamDataFileGroup || filegroup.type === FileGroupType.MemoryOptimizedDataFileGroup).map(filegroup => filegroup.name);
|
||||
this.rowDatafileGroupsOptions = this.objectInfo.filegroups?.filter(filegroup => filegroup.type === FileGroupType.RowsFileGroup).map(filegroup => filegroup.name);
|
||||
let index: number;
|
||||
if ((index = this.rowDatafileGroupsOptions.indexOf('PRIMARY')) !== -1) {
|
||||
this.rowDatafileGroupsOptions.unshift(this.rowDatafileGroupsOptions.splice(index, 1)[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removed the selected row from the respective table on its remove button click
|
||||
* @param table table component
|
||||
*/
|
||||
private async onRemoveDatabaseFileGroupsButtonClicked(table: azdata.TableComponent): Promise<void> {
|
||||
if (table === this.rowsFilegroupsTable) {
|
||||
if (this.rowsFilegroupsTable.selectedRows.length === 1) {
|
||||
const removeFilegroupIndex = this.objectInfo.filegroups.indexOf(this.rowDataFileGroupsTableRows[this.rowsFilegroupsTable.selectedRows[0]]);
|
||||
this.objectInfo.filegroups?.splice(removeFilegroupIndex, 1);
|
||||
var newData = this.getTableData(FileGroupType.RowsFileGroup);
|
||||
await this.rowsFilegroupNameInput.updateCssStyles({ 'visibility': 'hidden' });
|
||||
}
|
||||
}
|
||||
else if (table === this.filestreamFilegroupsTable) {
|
||||
if (this.filestreamFilegroupsTable.selectedRows.length === 1) {
|
||||
const removeFilegroupIndex = this.objectInfo.filegroups.indexOf(this.filestreamDataFileGroupsTableRows[this.filestreamFilegroupsTable.selectedRows[0]]);
|
||||
this.objectInfo.filegroups?.splice(removeFilegroupIndex, 1);
|
||||
var newData = this.getTableData(FileGroupType.FileStreamDataFileGroup);
|
||||
await this.filestreamFilegroupNameInput.updateCssStyles({ 'visibility': 'hidden' });
|
||||
}
|
||||
}
|
||||
else if (table === this.memoryOptimizedFilegroupsTable) {
|
||||
if (this.memoryOptimizedFilegroupsTable.selectedRows.length === 1) {
|
||||
const removeFilegroupIndex = this.objectInfo.filegroups.indexOf(this.memoryoptimizedFileGroupsTableRows[this.memoryOptimizedFilegroupsTable.selectedRows[0]]);
|
||||
this.objectInfo.filegroups?.splice(removeFilegroupIndex, 1);
|
||||
var newData = this.getTableData(FileGroupType.MemoryOptimizedDataFileGroup);
|
||||
await this.memoryOptimizedFilegroupNameInput.updateCssStyles({ 'visibility': 'hidden' });
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh the individual table rows object and table with updated data
|
||||
this.updateFileGroupsOptionsAndTableRows();
|
||||
await this.setTableData(table, newData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates input box for filegroup name
|
||||
* @param table table component
|
||||
* @param filegroupType filegroup type
|
||||
* @returns Input component
|
||||
*/
|
||||
private getFilegroupNameInput(table: azdata.TableComponent, filegroupType: FileGroupType): azdata.InputBoxComponent {
|
||||
return this.createInputBox(async (value) => {
|
||||
if (table.selectedRows.length === 1) {
|
||||
let fg = null;
|
||||
if (table === this.rowsFilegroupsTable) {
|
||||
fg = this.rowDataFileGroupsTableRows[table.selectedRows[0]];
|
||||
} else if (table === this.filestreamFilegroupsTable) {
|
||||
fg = this.filestreamDataFileGroupsTableRows[table.selectedRows[0]];
|
||||
} else if (table === this.memoryOptimizedFilegroupsTable) {
|
||||
fg = this.memoryoptimizedFileGroupsTableRows[table.selectedRows[0]];
|
||||
}
|
||||
if (fg !== null && fg.id < 0) {
|
||||
fg.name = value;
|
||||
let data = this.getTableData(filegroupType);
|
||||
await this.setTableData(table, data);
|
||||
this.updateFileGroupsOptionsAndTableRows();
|
||||
}
|
||||
}
|
||||
}, {
|
||||
ariaLabel: '',
|
||||
inputType: 'text',
|
||||
enabled: true,
|
||||
value: '',
|
||||
width: 200,
|
||||
CSSStyles: { 'margin': '5px 0px 0px 10px', 'visibility': 'hidden' }
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the filegroup object to a data view object
|
||||
* Note: Cannot change properties(Read-only, Default, Autogrow All files) of empty Rows data filegroups, the filegroup must contain at least one file
|
||||
* Note: Cannot change properties(Read-only) of empty Filestream data filegroups, the filegroup must contain at least one file
|
||||
* @param filegroupType filegroup type
|
||||
* @returns data view object
|
||||
*/
|
||||
private getTableData(filegroupType: FileGroupType): any[] {
|
||||
let data: any[] = [];
|
||||
this.objectInfo.filegroups?.map(fileGroup => {
|
||||
const filesCount = this.objectInfo.files?.filter(file => file.fileGroup === fileGroup.name).length;
|
||||
if (filegroupType === FileGroupType.RowsFileGroup && fileGroup.type === filegroupType) {
|
||||
data.push([
|
||||
fileGroup.name,
|
||||
filesCount,
|
||||
{ checked: fileGroup.isReadOnly, enabled: (fileGroup.name !== 'PRIMARY' && filesCount > 0) },
|
||||
{ checked: fileGroup.isDefault, enabled: filesCount > 0 },
|
||||
{ checked: fileGroup.autogrowAllFiles, enabled: filesCount > 0 }
|
||||
]);
|
||||
} else if (fileGroup.type === FileGroupType.FileStreamDataFileGroup && fileGroup.type === filegroupType) {
|
||||
data.push([
|
||||
fileGroup.name,
|
||||
filesCount,
|
||||
{ checked: fileGroup.isReadOnly, enabled: filesCount > 0 },
|
||||
fileGroup.isDefault
|
||||
]);
|
||||
} else if (fileGroup.type === FileGroupType.MemoryOptimizedDataFileGroup && fileGroup.type === filegroupType) {
|
||||
data.push([
|
||||
fileGroup.name,
|
||||
filesCount
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region Database Properties - Options Tab
|
||||
private initializeOptionsGeneralSection(): void {
|
||||
let containers: azdata.Component[] = [];
|
||||
|
||||
@@ -16,6 +16,8 @@ export interface NewDatabaseFileDialogOptions {
|
||||
title: string;
|
||||
viewInfo: DatabaseViewInfo;
|
||||
files: DatabaseFile[];
|
||||
rowFilegroups: string[];
|
||||
filestreamFilegroups: string[];
|
||||
isNewFile: boolean;
|
||||
isEditingNewFile: boolean;
|
||||
databaseFile: DatabaseFile;
|
||||
@@ -144,7 +146,7 @@ export class DatabaseFileDialog extends DialogBase<DatabaseFile> {
|
||||
// Filegroup
|
||||
this.fileGroupDropdown = this.createDropdown(localizedConstants.FilegroupText, async (newValue) => {
|
||||
this.result.fileGroup = newValue;
|
||||
}, this.options.viewInfo.rowDataFileGroupsOptions, this.options.databaseFile.fileGroup, this.isEditingFile, DefaultInputWidth);
|
||||
}, this.options.rowFilegroups, this.options.databaseFile.fileGroup, this.isEditingFile, DefaultInputWidth);
|
||||
const sizeContainer = this.createLabelInputContainer(localizedConstants.FilegroupText, this.fileGroupDropdown);
|
||||
containers.push(sizeContainer);
|
||||
|
||||
@@ -315,7 +317,7 @@ export class DatabaseFileDialog extends DialogBase<DatabaseFile> {
|
||||
*/
|
||||
private async updateOptionsForSelectedFileType(selectedOption: string): Promise<void> {
|
||||
// Row Data defaults
|
||||
let fileGroupDdOptions = this.options.viewInfo.rowDataFileGroupsOptions;
|
||||
let fileGroupDdOptions = this.options.rowFilegroups;
|
||||
let fileGroupDdValue = this.result.fileGroup;
|
||||
let visibility = 'visible';
|
||||
let maxSizeGroupMarginTop = '0px';
|
||||
@@ -330,7 +332,7 @@ export class DatabaseFileDialog extends DialogBase<DatabaseFile> {
|
||||
}
|
||||
// File Stream
|
||||
else if (selectedOption === localizedConstants.FilestreamFileType) {
|
||||
fileGroupDdOptions = this.options.viewInfo.fileStreamFileGroupsOptions;
|
||||
fileGroupDdOptions = this.options.filestreamFilegroups;
|
||||
fileGroupDdValue = this.result.fileGroup;
|
||||
visibility = 'hidden';
|
||||
maxSizeGroupMarginTop = '-130px';
|
||||
|
||||
@@ -77,7 +77,7 @@ export abstract class DialogBase<DialogResult> {
|
||||
|
||||
protected onFormFieldChange(): void { }
|
||||
|
||||
protected get removeButtonEnabled(): boolean { return true; }
|
||||
protected removeButtonEnabled(table: azdata.TableComponent): boolean { return true; }
|
||||
|
||||
protected validateInput(): Promise<string[]> { return Promise.resolve([]); }
|
||||
|
||||
@@ -244,7 +244,7 @@ export abstract class DialogBase<DialogResult> {
|
||||
return table;
|
||||
}
|
||||
|
||||
protected async setTableData(table: azdata.TableComponent, data: any[][], maxRowCount: number = DefaultMaxTableRowCount) {
|
||||
protected async setTableData(table: azdata.TableComponent, data: any[][], maxRowCount: number = DefaultMaxTableRowCount): Promise<void> {
|
||||
await table.updateProperties({
|
||||
data: data,
|
||||
height: getTableHeight(data.length, DefaultMinTableRowCount, maxRowCount)
|
||||
@@ -314,7 +314,7 @@ export abstract class DialogBase<DialogResult> {
|
||||
buttonComponents.push(removeButtonComponent);
|
||||
|
||||
this.disposables.push(table.onRowSelected(() => {
|
||||
const isRemoveButtonEnabled = this.removeButtonEnabled;
|
||||
const isRemoveButtonEnabled = this.removeButtonEnabled(table);
|
||||
updateButtons(isRemoveButtonEnabled);
|
||||
}));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user