Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for DATA_COMPRESSION and SORT_IN_TEMPDB SQL Server index options #30831

Merged
merged 2 commits into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions src/EFCore.SqlServer/DataCompressionType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

// ReSharper disable once CheckNamespace
namespace Microsoft.EntityFrameworkCore;

/// <summary>
/// Indicates type of data compression used on a index.
/// </summary>
/// <remarks>
/// See <see href="https://docs.microsoft.com/sql/relational-databases/data-compression">Data Compression</see> for more information on data compression.
/// </remarks>
public enum DataCompressionType
{
/// <summary>
/// Index is not compressed.
/// </summary>
None,

/// <summary>
/// Index is compressed by using row compression.
/// </summary>
Row,

/// <summary>
/// Index is compressed by using page compression.
/// </summary>
Page
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ private static readonly MethodInfo IndexHasFillFactorMethodInfo
= typeof(SqlServerIndexBuilderExtensions).GetRuntimeMethod(
nameof(SqlServerIndexBuilderExtensions.HasFillFactor), new[] { typeof(IndexBuilder), typeof(int) })!;

private static readonly MethodInfo IndexIsSortedInTempDbInfo
= typeof(SqlServerIndexBuilderExtensions).GetRuntimeMethod(
nameof(SqlServerIndexBuilderExtensions.IsSortedInTempDb), new[] { typeof(IndexBuilder), typeof(bool) })!;

private static readonly MethodInfo IndexUseDataCompressionInfo
= typeof(SqlServerIndexBuilderExtensions).GetRuntimeMethod(
nameof(SqlServerIndexBuilderExtensions.UseDataCompression), new[] { typeof(IndexBuilder), typeof(DataCompressionType) })!;

private static readonly MethodInfo KeyIsClusteredMethodInfo
= typeof(SqlServerKeyBuilderExtensions).GetRuntimeMethod(
nameof(SqlServerKeyBuilderExtensions.IsClustered), new[] { typeof(KeyBuilder), typeof(bool) })!;
Expand Down Expand Up @@ -347,6 +355,8 @@ protected override bool IsHandledByConvention(IProperty property, IAnnotation an

SqlServerAnnotationNames.Include => new MethodCallCodeFragment(IndexIncludePropertiesMethodInfo, annotation.Value),
SqlServerAnnotationNames.FillFactor => new MethodCallCodeFragment(IndexHasFillFactorMethodInfo, annotation.Value),
SqlServerAnnotationNames.SortedInTempDb => new MethodCallCodeFragment(IndexIsSortedInTempDbInfo, annotation.Value),
SqlServerAnnotationNames.DataCompression => new MethodCallCodeFragment(IndexUseDataCompressionInfo, annotation.Value),

_ => null
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ public override void Generate(IIndex index, CSharpRuntimeAnnotationCodeGenerator
annotations.Remove(SqlServerAnnotationNames.CreatedOnline);
annotations.Remove(SqlServerAnnotationNames.Include);
annotations.Remove(SqlServerAnnotationNames.FillFactor);
annotations.Remove(SqlServerAnnotationNames.SortedInTempDb);
annotations.Remove(SqlServerAnnotationNames.DataCompression);
}

base.Generate(index, parameters);
Expand All @@ -119,6 +121,8 @@ public override void Generate(ITableIndex index, CSharpRuntimeAnnotationCodeGene
annotations.Remove(SqlServerAnnotationNames.CreatedOnline);
annotations.Remove(SqlServerAnnotationNames.Include);
annotations.Remove(SqlServerAnnotationNames.FillFactor);
annotations.Remove(SqlServerAnnotationNames.SortedInTempDb);
annotations.Remove(SqlServerAnnotationNames.DataCompression);
}

base.Generate(index, parameters);
Expand Down
172 changes: 172 additions & 0 deletions src/EFCore.SqlServer/Extensions/SqlServerIndexBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -391,4 +391,176 @@ public static bool CanSetFillFactor(
int? fillFactor,
bool fromDataAnnotation = false)
=> indexBuilder.CanSetAnnotation(SqlServerAnnotationNames.FillFactor, fillFactor, fromDataAnnotation);

/// <summary>
/// Configures whether the index is created with sort in tempdb option when targeting SQL Server.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-modeling">Modeling entity types and relationships</see>, and
/// <see href="https://aka.ms/efcore-docs-sqlserver">Accessing SQL Server and SQL Azure databases with EF Core</see>
/// for more information and examples.
/// </remarks>
/// <param name="indexBuilder">The builder for the index being configured.</param>
/// <param name="sortedInTempDb">A value indicating whether the index is created with sort in tempdb option.</param>
/// <returns>A builder to further configure the index.</returns>
public static IndexBuilder IsSortedInTempDb(this IndexBuilder indexBuilder, bool sortedInTempDb = true)
{
indexBuilder.Metadata.SetIsSortedInTempDb(sortedInTempDb);

return indexBuilder;
}

/// <summary>
/// Configures whether the index is created with sort in tempdb option when targeting SQL Server.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-modeling">Modeling entity types and relationships</see>, and
/// <see href="https://aka.ms/efcore-docs-sqlserver">Accessing SQL Server and SQL Azure databases with EF Core</see>
/// for more information and examples.
/// </remarks>
/// <param name="indexBuilder">The builder for the index being configured.</param>
/// <param name="sortedInTempDb">A value indicating whether the index is created with sort in tempdb option.</param>
/// <returns>A builder to further configure the index.</returns>
public static IndexBuilder<TEntity> IsSortedInTempDb<TEntity>(
this IndexBuilder<TEntity> indexBuilder,
bool sortedInTempDb = true)
=> (IndexBuilder<TEntity>)IsSortedInTempDb((IndexBuilder)indexBuilder, sortedInTempDb);

/// <summary>
/// Configures whether the index is created with sort in tempdb option when targeting SQL Server.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-modeling">Modeling entity types and relationships</see>, and
/// <see href="https://aka.ms/efcore-docs-sqlserver">Accessing SQL Server and SQL Azure databases with EF Core</see>
/// for more information and examples.
/// </remarks>
/// <param name="indexBuilder">The builder for the index being configured.</param>
/// <param name="sortedInTempDb">A value indicating whether the index is created with sort in tempdb option.</param>
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
/// <returns>
/// The same builder instance if the configuration was applied,
/// <see langword="null" /> otherwise.
/// </returns>
public static IConventionIndexBuilder? IsSortedInTempDb(
this IConventionIndexBuilder indexBuilder,
bool? sortedInTempDb,
bool fromDataAnnotation = false)
{
if (indexBuilder.CanSetIsSortedInTempDb(sortedInTempDb, fromDataAnnotation))
{
indexBuilder.Metadata.SetIsSortedInTempDb(sortedInTempDb, fromDataAnnotation);

return indexBuilder;
}

return null;
}

/// <summary>
/// Returns a value indicating whether the index can be configured with sort in tempdb option when targeting SQL Server.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-modeling">Modeling entity types and relationships</see>, and
/// <see href="https://aka.ms/efcore-docs-sqlserver">Accessing SQL Server and SQL Azure databases with EF Core</see>
/// for more information and examples.
/// </remarks>
/// <param name="indexBuilder">The builder for the index being configured.</param>
/// <param name="sortedInTempDb">A value indicating whether the index is created with sort in tempdb option.</param>
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
/// <returns>
/// The same builder instance if the configuration was applied,
/// <see langword="null" /> otherwise.
/// </returns>
/// <returns><see langword="true" /> if the index can be configured with sort in tempdb option when targeting SQL Server.</returns>
public static bool CanSetIsSortedInTempDb(
this IConventionIndexBuilder indexBuilder,
bool? sortedInTempDb,
bool fromDataAnnotation = false)
=> indexBuilder.CanSetAnnotation(SqlServerAnnotationNames.SortedInTempDb, sortedInTempDb, fromDataAnnotation);

/// <summary>
/// Configures whether the index is created with data compression option when targeting SQL Server.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-modeling">Modeling entity types and relationships</see>, and
/// <see href="https://aka.ms/efcore-docs-sqlserver">Accessing SQL Server and SQL Azure databases with EF Core</see>
/// for more information and examples.
/// </remarks>
/// <param name="indexBuilder">The builder for the index being configured.</param>
/// <param name="dataCompressionType">A value indicating the data compression option to be used.</param>
/// <returns>A builder to further configure the index.</returns>
public static IndexBuilder UseDataCompression(this IndexBuilder indexBuilder, DataCompressionType dataCompressionType)
{
indexBuilder.Metadata.SetDataCompression(dataCompressionType);

return indexBuilder;
}

/// <summary>
/// Configures whether the index is created with data compression option when targeting SQL Server.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-modeling">Modeling entity types and relationships</see>, and
/// <see href="https://aka.ms/efcore-docs-sqlserver">Accessing SQL Server and SQL Azure databases with EF Core</see>
/// for more information and examples.
/// </remarks>
/// <param name="indexBuilder">The builder for the index being configured.</param>
/// <param name="dataCompressionType">A value indicating the data compression option to be used.</param>
/// <returns>A builder to further configure the index.</returns>
public static IndexBuilder<TEntity> UseDataCompression<TEntity>(
this IndexBuilder<TEntity> indexBuilder,
DataCompressionType dataCompressionType)
=> (IndexBuilder<TEntity>)UseDataCompression((IndexBuilder)indexBuilder, dataCompressionType);

/// <summary>
/// Configures whether the index is created with data compression option when targeting SQL Server.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-modeling">Modeling entity types and relationships</see>, and
/// <see href="https://aka.ms/efcore-docs-sqlserver">Accessing SQL Server and SQL Azure databases with EF Core</see>
/// for more information and examples.
/// </remarks>
/// <param name="indexBuilder">The builder for the index being configured.</param>
/// <param name="dataCompressionType">A value indicating the data compression option to be used.</param>
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
/// <returns>
/// The same builder instance if the configuration was applied,
/// <see langword="null" /> otherwise.
/// </returns>
public static IConventionIndexBuilder? UseDataCompression(
this IConventionIndexBuilder indexBuilder,
DataCompressionType? dataCompressionType,
bool fromDataAnnotation = false)
{
if (indexBuilder.CanSetDataCompression(dataCompressionType, fromDataAnnotation))
{
indexBuilder.Metadata.SetDataCompression(dataCompressionType, fromDataAnnotation);

return indexBuilder;
}

return null;
}

/// <summary>
/// Returns a value indicating whether the index can be configured with data compression option when targeting SQL Server.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-modeling">Modeling entity types and relationships</see>, and
/// <see href="https://aka.ms/efcore-docs-sqlserver">Accessing SQL Server and SQL Azure databases with EF Core</see>
/// for more information and examples.
/// </remarks>
/// <param name="indexBuilder">The builder for the index being configured.</param>
/// <param name="dataCompressionType">A value indicating the data compression option to be used.</param>
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
/// <returns>
/// The same builder instance if the configuration was applied,
/// <see langword="null" /> otherwise.
/// </returns>
/// <returns><see langword="true" /> if the index can be configured with data compression option when targeting SQL Server.</returns>
public static bool CanSetDataCompression(
this IConventionIndexBuilder indexBuilder,
DataCompressionType? dataCompressionType,
bool fromDataAnnotation = false)
=> indexBuilder.CanSetAnnotation(SqlServerAnnotationNames.DataCompression, dataCompressionType, fromDataAnnotation);
}
132 changes: 132 additions & 0 deletions src/EFCore.SqlServer/Extensions/SqlServerIndexExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -297,4 +297,136 @@ public static void SetFillFactor(this IMutableIndex index, int? fillFactor)
/// <returns>The <see cref="ConfigurationSource" /> for whether the index uses the fill factor.</returns>
public static ConfigurationSource? GetFillFactorConfigurationSource(this IConventionIndex index)
=> index.FindAnnotation(SqlServerAnnotationNames.FillFactor)?.GetConfigurationSource();

/// <summary>
/// Returns a value indicating whether the index is sorted in tempdb.
/// </summary>
/// <param name="index">The index.</param>
/// <returns><see langword="true" /> if the index is sorted in tempdb.</returns>
public static bool? GetIsSortedInTempDb(this IReadOnlyIndex index)
=> (index is RuntimeIndex)
? throw new InvalidOperationException(CoreStrings.RuntimeModelMissingData)
: (bool?)index[SqlServerAnnotationNames.SortedInTempDb];

/// <summary>
/// Returns a value indicating whether the index is sorted in tempdb.
/// </summary>
/// <param name="index">The index.</param>
/// <param name="storeObject">The identifier of the store object.</param>
/// <returns><see langword="true" /> if the index is sorted in tempdb.</returns>
public static bool? GetIsSortedInTempDb(this IReadOnlyIndex index, in StoreObjectIdentifier storeObject)
{
if (index is RuntimeIndex)
{
throw new InvalidOperationException(CoreStrings.RuntimeModelMissingData);
}

var annotation = index.FindAnnotation(SqlServerAnnotationNames.SortedInTempDb);
if (annotation != null)
{
return (bool?)annotation.Value;
}

var sharedTableRootIndex = index.FindSharedObjectRootIndex(storeObject);
return sharedTableRootIndex?.GetIsSortedInTempDb(storeObject);
}

/// <summary>
/// Sets a value indicating whether the index is sorted in tempdb.
/// </summary>
/// <param name="index">The index.</param>
/// <param name="sortedInTempDb">The value to set.</param>
public static void SetIsSortedInTempDb(this IMutableIndex index, bool? sortedInTempDb)
=> index.SetAnnotation(
SqlServerAnnotationNames.SortedInTempDb,
sortedInTempDb);

/// <summary>
/// Sets a value indicating whether the index is sorted in tempdb.
/// </summary>
/// <param name="index">The index.</param>
/// <param name="sortedInTempDb">The value to set.</param>
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
/// <returns>The configured value.</returns>
public static bool? SetIsSortedInTempDb(
this IConventionIndex index,
bool? sortedInTempDb,
bool fromDataAnnotation = false)
=> (bool?)index.SetAnnotation(
SqlServerAnnotationNames.SortedInTempDb,
sortedInTempDb,
fromDataAnnotation)?.Value;

/// <summary>
/// Returns the <see cref="ConfigurationSource" /> for whether the index is sorted in tempdb.
/// </summary>
/// <param name="index">The index.</param>
/// <returns>The <see cref="ConfigurationSource" /> for whether the index is sorted in tempdb.</returns>
public static ConfigurationSource? GetIsSortedInTempDbConfigurationSource(this IConventionIndex index)
=> index.FindAnnotation(SqlServerAnnotationNames.SortedInTempDb)?.GetConfigurationSource();

/// <summary>
/// Returns the data compression that the index uses.
/// </summary>
/// <param name="index">The index.</param>
/// <returns>The data compression that the index uses</returns>
public static DataCompressionType? GetDataCompression(this IReadOnlyIndex index)
=> (index is RuntimeIndex)
? throw new InvalidOperationException(CoreStrings.RuntimeModelMissingData)
: (DataCompressionType?)index[SqlServerAnnotationNames.DataCompression];

/// <summary>
/// Returns the data compression that the index uses.
/// </summary>
/// <param name="index">The index.</param>
/// <param name="storeObject">The identifier of the store object.</param>
/// <returns>The data compression that the index uses</returns>
public static DataCompressionType? GetDataCompression(this IReadOnlyIndex index, in StoreObjectIdentifier storeObject)
{
if (index is RuntimeIndex)
{
throw new InvalidOperationException(CoreStrings.RuntimeModelMissingData);
}

var annotation = index.FindAnnotation(SqlServerAnnotationNames.DataCompression);
if (annotation != null)
{
return (DataCompressionType?)annotation.Value;
}

var sharedTableRootIndex = index.FindSharedObjectRootIndex(storeObject);
return sharedTableRootIndex?.GetDataCompression(storeObject);
}

/// <summary>
/// Sets a value indicating the data compression the index uses.
/// </summary>
/// <param name="index">The index.</param>
/// <param name="dataCompression">The value to set.</param>
public static void SetDataCompression(this IMutableIndex index, DataCompressionType? dataCompression) => index.SetAnnotation(
SqlServerAnnotationNames.DataCompression,
dataCompression);

/// <summary>
/// Sets a value indicating the data compression the index uses.
/// </summary>
/// <param name="index">The index.</param>
/// <param name="dataCompression">The value to set.</param>
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
/// <returns>The configured value.</returns>
public static DataCompressionType? SetDataCompression(
this IConventionIndex index,
DataCompressionType? dataCompression,
bool fromDataAnnotation = false) => (DataCompressionType?)index.SetAnnotation(
SqlServerAnnotationNames.DataCompression,
dataCompression,
fromDataAnnotation)?.Value;

/// <summary>
/// Returns the <see cref="ConfigurationSource" /> for the data compression the index uses.
/// </summary>
/// <param name="index">The index.</param>
/// <returns>The <see cref="ConfigurationSource" /> for the data compression the index uses.</returns>
public static ConfigurationSource? GetDataCompressionConfigurationSource(this IConventionIndex index)
=> index.FindAnnotation(SqlServerAnnotationNames.DataCompression)?.GetConfigurationSource();
}
Loading