[Table Designer] support hash index and column store index (#1672)

This commit is contained in:
Hai Cao
2022-09-06 13:32:43 -07:00
committed by GitHub
parent 2480cf38da
commit 8f26a20950
12 changed files with 907 additions and 1 deletions

View File

@@ -14,6 +14,7 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
public const string ForeignKeys = "foreignKeys";
public const string CheckConstraints = "checkConstraints";
public const string Indexes = "indexes";
public const string ColumnStoreIndexes = "columnStoreIndexes";
public const string EdgeConstraints = "edgeConstraints";
public const string GraphTableType = "graphTableType";
public const string IsSystemVersioningEnabled = "isSystemVersioningEnabled";
@@ -86,6 +87,8 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
public const string Enabled = "enabled";
public const string IsUnique = "isUnique";
public const string IsClustered = "isClustered";
public const string IsHash = "isHash";
public const string BucketCount = "bucketCount";
public const string Columns = "columns";
public const string IncludedColumns = "includedColumns";
public const string FilterPredicate = "filterPredicate";
@@ -103,6 +106,21 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
public const string Column = "column";
}
public static class ColumnStoreIndexPropertyNames
{
public const string Name = "name";
public const string Description = "description";
public const string IsClustered = "isClustered";
public const string Columns = "columns";
public const string FilterPredicate = "filterPredicate";
public const string ColumnsDisplayValue = "columnsDisplayValue";
}
public static class ColumnStoreIndexColumnSpecificationPropertyNames
{
public const string Column = "column";
}
public static class EdgeConstraintPropertyNames
{
public const string Name = "name";

View File

@@ -0,0 +1,18 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
namespace Microsoft.SqlTools.ServiceLayer.TableDesigner.Contracts
{
/// <summary>
/// Represents a component/property with tab info in the table designer
/// </summary>
public class DesignerDataPropertyWithTabInfo : DesignerDataPropertyInfo
{
/// <summary>
/// The tab of the property
/// </summary>
public string Tab { get; set; }
}
}

View File

@@ -21,6 +21,7 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner.Contracts
public BuiltinTableOptions IndexTableOptions { get; set; } = new BuiltinTableOptions();
public BuiltinTableOptions IndexColumnSpecificationTableOptions { get; set; } = new BuiltinTableOptions();
public List<DesignerDataPropertyInfo> AdditionalPrimaryKeyProperties { get; set; } = new List<DesignerDataPropertyInfo>();
public List<DesignerDataPropertyWithTabInfo> AdditionalComponents { get; set; } = new List<DesignerDataPropertyWithTabInfo>();
public BuiltinTableOptions PrimaryKeyColumnSpecificationTableOptions = new BuiltinTableOptions();
public List<DesignerTabView> AdditionalTabs { get; } = new List<DesignerTabView>();
public bool UseAdvancedSaveMode { get; set; }

View File

@@ -0,0 +1,28 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
namespace Microsoft.SqlTools.ServiceLayer.TableDesigner.Contracts
{
/// <summary>
/// The view model of column store index.
/// </summary>
public class ColumnStoreIndexViewModel : ObjectViewModelBase
{
public CheckBoxProperties IsClustered { get; set; } = new CheckBoxProperties();
public InputBoxProperties ColumnsDisplayValue { get; set; } = new InputBoxProperties();
public InputBoxProperties FilterPredicate { get; set; } = new InputBoxProperties();
public TableComponentProperties<ColumnStoreIndexedColumnSpecification> Columns { get; set; } = new TableComponentProperties<ColumnStoreIndexedColumnSpecification>();
}
public class ColumnStoreIndexedColumnSpecification
{
public DropdownProperties Column { get; set; } = new DropdownProperties();
public CheckBoxProperties Ascending { get; set; } = new CheckBoxProperties();
}
}

View File

@@ -15,7 +15,9 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner.Contracts
public CheckBoxProperties IsClustered { get; set; } = new CheckBoxProperties();
public CheckBoxProperties IsUnique { get; set; } = new CheckBoxProperties();
public CheckBoxProperties IsHash { get; set; } = new CheckBoxProperties();
public InputBoxProperties BucketCount { get; set; } = new InputBoxProperties();
public InputBoxProperties ColumnsDisplayValue { get; set; } = new InputBoxProperties();
public InputBoxProperties FilterPredicate { get; set; } = new InputBoxProperties();

View File

@@ -42,6 +42,7 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner.Contracts
public TableComponentProperties<EdgeConstraintViewModel> EdgeConstraints { get; set; } = new TableComponentProperties<EdgeConstraintViewModel>();
public TableComponentProperties<IndexViewModel> Indexes { get; set; } = new TableComponentProperties<IndexViewModel>();
public TableComponentProperties<ColumnStoreIndexViewModel> ColumnStoreIndexes { get; set; } = new TableComponentProperties<ColumnStoreIndexViewModel>();
public InputBoxProperties Script { get; set; } = new InputBoxProperties();
}

View File

@@ -245,6 +245,9 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
case TablePropertyNames.Indexes:
table.Indexes.AddNew();
break;
case TablePropertyNames.ColumnStoreIndexes:
table.ColumnStoreIndexes.AddNew();
break;
case TablePropertyNames.EdgeConstraints:
table.EdgeConstraints.AddNew();
break;
@@ -290,6 +293,16 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
break;
}
break;
case TablePropertyNames.ColumnStoreIndexes:
switch (propertyNameL2)
{
case ColumnStoreIndexPropertyNames.Columns:
table.ColumnStoreIndexes.Items[indexL1].AddNewColumnSpecification();
break;
default:
break;
}
break;
case TablePropertyNames.EdgeConstraints:
switch (propertyNameL2)
{
@@ -329,6 +342,9 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
case TablePropertyNames.Indexes:
table.Indexes.RemoveAt(objIndex);
break;
case TablePropertyNames.ColumnStoreIndexes:
table.ColumnStoreIndexes.RemoveAt(objIndex);
break;
case TablePropertyNames.EdgeConstraints:
table.EdgeConstraints.RemoveAt(objIndex);
break;
@@ -370,6 +386,16 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
break;
}
break;
case TablePropertyNames.ColumnStoreIndexes:
switch (propertyNameL2)
{
case ColumnStoreIndexPropertyNames.Columns:
table.ColumnStoreIndexes.Items[indexL1].RemoveColumnSpecification(indexL2);
break;
default:
break;
}
break;
case TablePropertyNames.EdgeConstraints:
switch (propertyNameL2)
{
@@ -612,6 +638,32 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
case IndexPropertyNames.FilterPredicate:
sqlIndex.FilterPredicate = GetStringValue(newValue);
break;
case IndexPropertyNames.IsHash:
sqlIndex.IsHash = GetBooleanValue(newValue);
break;
case IndexPropertyNames.BucketCount:
sqlIndex.BucketCount = GetInt32Value(newValue);
break;
default:
break;
}
break;
case TablePropertyNames.ColumnStoreIndexes:
var csIndex = table.ColumnStoreIndexes.Items[indexL1];
switch (propertyNameL2)
{
case ColumnStoreIndexPropertyNames.IsClustered:
csIndex.IsClustered = GetBooleanValue(newValue);
break;
case ColumnStoreIndexPropertyNames.Name:
csIndex.Name = GetStringValue(newValue);
break;
case ColumnStoreIndexPropertyNames.Description:
csIndex.Description = GetStringValue(newValue);
break;
case ColumnStoreIndexPropertyNames.FilterPredicate:
csIndex.FilterPredicate = GetStringValue(newValue);
break;
default:
break;
}
@@ -711,6 +763,24 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
break;
}
break;
case TablePropertyNames.ColumnStoreIndexes:
var csIndex = table.ColumnStoreIndexes.Items[indexL1];
switch (propertyNameL2)
{
case ColumnStoreIndexPropertyNames.Columns:
switch (propertyNameL3)
{
case ColumnStoreIndexColumnSpecificationPropertyNames.Column:
csIndex.UpdateColumnName(indexL2, GetStringValue(newValue));
break;
default:
break;
}
break;
default:
break;
}
break;
case TablePropertyNames.EdgeConstraints:
var constraint = table.EdgeConstraints.Items[indexL1];
switch (propertyNameL2)
@@ -930,10 +1000,15 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
indexVM.IsClustered.Checked = index.IsClustered;
indexVM.Enabled.Checked = index.Enabled;
indexVM.IsUnique.Checked = index.IsUnique;
indexVM.IsHash.Checked = index.IsHash;
indexVM.IsHash.Enabled = table.IsMemoryOptimized || index.IsHash;
indexVM.BucketCount.Value = index.BucketCount?.ToString();
indexVM.BucketCount.Enabled = table.IsMemoryOptimized || index.IsHash;
foreach (var columnSpec in index.Columns)
{
var columnSpecVM = new IndexedColumnSpecification();
columnSpecVM.Ascending.Checked = columnSpec.IsAscending;
columnSpecVM.Ascending.Enabled = index.CanEditIsAscending;
columnSpecVM.Column.Value = columnSpec.Column;
columnSpecVM.Column.Values = tableDesigner.GetColumnsForTable(table.FullName).ToList();
indexVM.Columns.Data.Add(columnSpecVM);
@@ -957,6 +1032,35 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
tableViewModel.Indexes.Data.Add(indexVM);
}
foreach (var index in table.ColumnStoreIndexes.Items)
{
var indexVM = new ColumnStoreIndexViewModel();
indexVM.Name.Value = index.Name;
indexVM.Description.Value = index.Description;
indexVM.Description.Enabled = index.CanEditDescription;
indexVM.IsClustered.Checked = index.IsClustered;
indexVM.FilterPredicate.Value = index.FilterPredicate;
indexVM.FilterPredicate.Enabled = !index.IsClustered || index.FilterPredicate != null;
indexVM.Columns.Enabled = !index.IsClustered || index.Columns.Count() > 0;
indexVM.Columns.CanAddRows = !index.IsClustered;
indexVM.ColumnsDisplayValue.Enabled = false;
// avoid populating columns for CLUSTERED column store index
if (!index.IsClustered)
{
indexVM.ColumnsDisplayValue.Value = index.ColumnsDisplayValue;
foreach (var columnSpec in index.Columns)
{
var columnSpecVM = new ColumnStoreIndexedColumnSpecification();
columnSpecVM.Column.Value = columnSpec.Column;
columnSpecVM.Column.Values = tableDesigner.GetColumnsForTable(table.FullName).ToList();
indexVM.Columns.Data.Add(columnSpecVM);
}
}
tableViewModel.ColumnStoreIndexes.Data.Add(indexVM);
}
foreach (var constraint in table.EdgeConstraints.Items)
{
var constraintVM = new EdgeConstraintViewModel();
@@ -993,6 +1097,7 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
this.SetForeignKeysViewInfo(view);
this.SetCheckConstraintsViewInfo(view);
this.SetIndexesViewInfo(view);
this.SetColumnStoreIndexesViewInfo(view);
this.SetGraphTableViewInfo(view, tableDesigner);
this.SetEdgeConstraintsViewInfo(view, tableDesigner);
this.SetTemporalTableViewInfo(view, tableDesigner);
@@ -1233,6 +1338,29 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
}
},
new DesignerDataPropertyInfo()
{
PropertyName = IndexPropertyNames.IsHash,
Description = SR.IndexIsHashPropertyDescription,
ComponentType = DesignerComponentType.Checkbox,
Group = SR.HashIndexGroupTitle,
ComponentProperties = new CheckBoxProperties()
{
Title = SR.IndexIsHashPropertyTitle
}
},
new DesignerDataPropertyInfo()
{
PropertyName = IndexPropertyNames.BucketCount,
Description = SR.IndexBucketCountPropertyDescription,
ComponentType = DesignerComponentType.Input,
Group = SR.HashIndexGroupTitle,
ComponentProperties = new InputBoxProperties()
{
Title = SR.IndexBucketCountPropertyTitle,
Width = 200
}
},
new DesignerDataPropertyInfo()
{
PropertyName = IndexPropertyNames.IncludedColumns,
Description = SR.IndexIncludedColumnsPropertyDescription,
@@ -1283,6 +1411,109 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
view.IndexColumnSpecificationTableOptions.CanInsertRows = false;
}
private void SetColumnStoreIndexesViewInfo(TableDesignerView view)
{
var columnStoreIndexesTableProperties = new TableComponentProperties<ColumnStoreIndexViewModel>()
{
Title = SR.TableDesignerColumnStoreIndexesTableTitle,
ObjectTypeDisplayName = SR.TableDesignerColumnStoreIndexObjectType,
LabelForAddNewButton = SR.AddNewColumnStoreIndexLabel
};
columnStoreIndexesTableProperties.Columns.AddRange(new string[] { ColumnStoreIndexPropertyNames.Name, ColumnStoreIndexPropertyNames.ColumnsDisplayValue, ColumnStoreIndexPropertyNames.IsClustered });
columnStoreIndexesTableProperties.ItemProperties.AddRange(new DesignerDataPropertyInfo[] {
new DesignerDataPropertyInfo()
{
PropertyName = ColumnStoreIndexPropertyNames.Name,
Description = SR.ColumnStoreIndexNamePropertyDescription,
ComponentType = DesignerComponentType.Input,
ComponentProperties = new InputBoxProperties()
{
Title = SR.ColumnStoreIndexNamePropertyTitle,
Width = 200
}
},
new DesignerDataPropertyInfo()
{
PropertyName = ColumnStoreIndexPropertyNames.Description,
Description = SR.ColumnStoreIndexDescriptionPropertyDescription,
ComponentType = DesignerComponentType.Input,
ComponentProperties = new InputBoxProperties()
{
Title = SR.ColumnStoreIndexDescriptionPropertyTitle,
Width = 200
}
},
new DesignerDataPropertyInfo()
{
PropertyName = ColumnStoreIndexPropertyNames.IsClustered,
Description = SR.ColumnStoreIndexIsClusteredPropertyDescription,
ComponentType = DesignerComponentType.Checkbox,
ComponentProperties = new CheckBoxProperties()
{
Title = SR.ColumnStoreIndexIsClusteredPropertyTitle
}
},
new DesignerDataPropertyInfo()
{
PropertyName = ColumnStoreIndexPropertyNames.FilterPredicate,
Description = SR.ColumnStoreIndexFilterPredicatePropertyDescription,
ComponentType = DesignerComponentType.Input,
ComponentProperties = new InputBoxProperties()
{
Title = SR.ColumnStoreIndexFilterPredicatePropertyTitle,
Width = 200
}
},
new DesignerDataPropertyInfo()
{
PropertyName = ColumnStoreIndexPropertyNames.ColumnsDisplayValue,
ShowInPropertiesView = false,
ComponentType = DesignerComponentType.Input,
ComponentProperties = new InputBoxProperties()
{
Title = SR.TableDesignerColumnsDisplayValueTitle,
Width = 200
}
},
new DesignerDataPropertyInfo()
{
PropertyName = ColumnStoreIndexPropertyNames.Columns,
Description = SR.ColumnStoreIndexColumnsPropertyDescription,
ComponentType = DesignerComponentType.Table,
Group = SR.ColumnStoreIndexColumnsGroupTitle,
ComponentProperties = new TableComponentProperties<IndexIncludedColumnSpecification>()
{
AriaLabel = SR.ColumnStoreIndexColumnsGroupTitle,
Columns = new List<string> () { ColumnStoreIndexColumnSpecificationPropertyNames.Column},
LabelForAddNewButton = SR.ColumnStoreIndexAddColumn,
ItemProperties = new List<DesignerDataPropertyInfo>()
{
new DesignerDataPropertyInfo()
{
PropertyName = ColumnStoreIndexColumnSpecificationPropertyNames.Column,
ComponentType = DesignerComponentType.Dropdown,
ComponentProperties = new DropdownProperties()
{
Title = SR.ColumnStoreIndexColumnPropertyName,
Width = 150
}
},
}
}
}
});
var columnStoreIndexesComponent = new DesignerDataPropertyWithTabInfo()
{
PropertyName = TablePropertyNames.ColumnStoreIndexes,
Tab = TablePropertyNames.Indexes,
ComponentType = DesignerComponentType.Table,
ComponentProperties = columnStoreIndexesTableProperties,
ShowInPropertiesView = false
};
view.AdditionalComponents.Add(columnStoreIndexesComponent);
}
private void SetGraphTableViewInfo(TableDesignerView view, Dac.TableDesigner tableDesigner)
{
if (tableDesigner.IsGraphTableSupported && (tableDesigner.IsNewTable || tableDesigner.TableViewModel.IsEdge || tableDesigner.TableViewModel.IsNode))

View File

@@ -39,7 +39,11 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
new ColumnCanOnlyAppearOnceInIndexIncludedColumnsRule(),
new ColumnCannotDuplicateWitIndexKeyColumnsRule(),
new ComputedColumnNeedToBePersistedAndNotNullInPrimaryKeyRule(),
new ComputedColumnNeedToBePersistedInForeignKeyRule()
new ComputedColumnNeedToBePersistedInForeignKeyRule(),
new HashIndexNotSupportedInNonMemoryOptimizedTableRule(),
new HashIndexMustHaveBucketCountRule(),
new ColumnCanOnlyAppearOnceInNonClusteredColumnStoreIndexRule(),
new NonClusteredColumnStoreIndexMustHaveColumnsRule()
};
/// <summary>
@@ -754,4 +758,107 @@ namespace Microsoft.SqlTools.ServiceLayer.TableDesigner
return errors;
}
}
public class HashIndexNotSupportedInNonMemoryOptimizedTableRule : ITableDesignerValidationRule
{
public List<TableDesignerIssue> Run(Dac.TableDesigner designer)
{
var table = designer.TableViewModel;
var errors = new List<TableDesignerIssue>();
if (!table.IsMemoryOptimized)
{
for (int i = 0; i < table.Indexes.Items.Count; i++)
{
var index = table.Indexes.Items[i];
if (index.IsHash)
{
errors.Add(new TableDesignerIssue()
{
Description = SR.HashIndexNotSupportedInNonMemoryOptimizedTableRuleDescription(index.Name),
PropertyPath = new object[] { TablePropertyNames.Indexes, i, IndexPropertyNames.IsHash }
});
}
}
}
return errors;
}
}
public class HashIndexMustHaveBucketCountRule : ITableDesignerValidationRule
{
public List<TableDesignerIssue> Run(Dac.TableDesigner designer)
{
var table = designer.TableViewModel;
var errors = new List<TableDesignerIssue>();
for (int i = 0; i < table.Indexes.Items.Count; i++)
{
var index = table.Indexes.Items[i];
if (index.IsHash && !index.BucketCount.HasValue)
{
errors.Add(new TableDesignerIssue()
{
Description = SR.HashIndexMustHaveBucketCountRuleDescription(index.Name),
PropertyPath = new object[] { TablePropertyNames.Indexes, i, IndexPropertyNames.BucketCount }
});
}
}
return errors;
}
}
public class ColumnCanOnlyAppearOnceInNonClusteredColumnStoreIndexRule : ITableDesignerValidationRule
{
public List<TableDesignerIssue> Run(Dac.TableDesigner designer)
{
var table = designer.TableViewModel;
var errors = new List<TableDesignerIssue>();
for (int i = 0; i < table.ColumnStoreIndexes.Items.Count; i++)
{
var index = table.ColumnStoreIndexes.Items[i];
if (!index.IsClustered)
{
var existingColumns = new HashSet<string>();
for (int j = 0; j < index.Columns.Count; j++)
{
var columnSpec = index.Columns[j];
if (existingColumns.Contains(columnSpec.Column))
{
errors.Add(new TableDesignerIssue()
{
Description = SR.ColumnCanOnlyAppearOnceInNonClusteredColumnStoreIndexRuleDescription(columnSpec.Column, index.Name, j + 1),
PropertyPath = new object[] { TablePropertyNames.ColumnStoreIndexes, i, ColumnStoreIndexPropertyNames.Columns, j }
});
}
else
{
existingColumns.Add(columnSpec.Column);
}
}
}
}
return errors;
}
}
public class NonClusteredColumnStoreIndexMustHaveColumnsRule : ITableDesignerValidationRule
{
public List<TableDesignerIssue> Run(Dac.TableDesigner designer)
{
var table = designer.TableViewModel;
var errors = new List<TableDesignerIssue>();
for (int i = 0; i < table.ColumnStoreIndexes.Items.Count; i++)
{
var index = table.ColumnStoreIndexes.Items[i];
if (!index.IsClustered && index.Columns.Count == 0)
{
errors.Add(new TableDesignerIssue()
{
Description = SR.NonClusteredColumnStoreIndexMustHaveColumnsRuleDescription(index.Name),
PropertyPath = new object[] { TablePropertyNames.ColumnStoreIndexes, i }
});
}
}
return errors;
}
}
}