Skip to content

Commit

Permalink
Add support for index storage parameters (#2914)
Browse files Browse the repository at this point in the history
And scaffold storage parameters for both tables and indices.

Closes #2899
Closes #2913
  • Loading branch information
roji authored Oct 27, 2023
1 parent 30ed41e commit f021d6f
Show file tree
Hide file tree
Showing 9 changed files with 320 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ public static EntityTypeBuilder<TEntity> HasStorageParameter<TEntity>(
}

/// <summary>
/// Returns a value indicating whether the PostgreSQL storage parameter on the table created for this entity.
/// Returns a value indicating whether the PostgreSQL storage parameter is set on the table created for this entity.
/// </summary>
/// <remarks>
/// See https://www.postgresql.org/docs/current/static/sql-createtable.html#SQL-CREATETABLE-STORAGE-PARAMETERS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,97 @@ public static bool CanSetAreNullsDistinct(

#endregion NULLS distinct

#region Storage parameters

/// <summary>
/// Sets a PostgreSQL storage parameter on the index.
/// </summary>
/// <remarks>
/// See https://www.postgresql.org/docs/current/sql-createindex.html#SQL-CREATEINDEX-STORAGE-PARAMETERS
/// </remarks>
/// <param name="indexBuilder">The builder for the index being configured.</param>
/// <param name="parameterName">The name of the storage parameter.</param>
/// <param name="parameterValue">The value of the storage parameter.</param>
/// <returns>The same builder instance so that multiple calls can be chained.</returns>
public static IndexBuilder HasStorageParameter(
this IndexBuilder indexBuilder,
string parameterName,
object? parameterValue)
{
Check.NotNull(indexBuilder, nameof(indexBuilder));

indexBuilder.Metadata.SetStorageParameter(parameterName, parameterValue);

return indexBuilder;
}

/// <summary>
/// Sets a PostgreSQL storage parameter on the index.
/// </summary>
/// <remarks>
/// See https://www.postgresql.org/docs/current/sql-createindex.html#SQL-CREATEINDEX-STORAGE-PARAMETERS
/// </remarks>
/// <param name="indexBuilder">The builder for the index being configured.</param>
/// <param name="parameterName">The name of the storage parameter.</param>
/// <param name="parameterValue">The value of the storage parameter.</param>
/// <returns>The same builder instance so that multiple calls can be chained.</returns>
public static IndexBuilder<TEntity> HasStorageParameter<TEntity>(
this IndexBuilder<TEntity> indexBuilder,
string parameterName,
object? parameterValue)
where TEntity : class
=> (IndexBuilder<TEntity>)HasStorageParameter((IndexBuilder)indexBuilder, parameterName, parameterValue);

/// <summary>
/// Sets a PostgreSQL storage parameter on the index.
/// </summary>
/// <remarks>
/// See https://www.postgresql.org/docs/current/sql-createindex.html#SQL-CREATEINDEX-STORAGE-PARAMETERS
/// </remarks>
/// <param name="indexBuilder">The builder for the index being configured.</param>
/// <param name="parameterName">The name of the storage parameter.</param>
/// <param name="parameterValue">The value of the storage parameter.</param>
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
/// <returns><c>true</c> if the index can be configured with the method</returns>
public static IConventionIndexBuilder? HasStorageParameter(
this IConventionIndexBuilder indexBuilder,
string parameterName,
object? parameterValue,
bool fromDataAnnotation = false)
{
if (indexBuilder.CanSetStorageParameter(parameterName, parameterValue, fromDataAnnotation))
{
indexBuilder.Metadata.SetStorageParameter(parameterName, parameterValue);

return indexBuilder;
}

return null;
}

/// <summary>
/// Returns a value indicating whether the PostgreSQL storage parameter is set on the table created for this entity.
/// </summary>
/// <remarks>
/// See https://www.postgresql.org/docs/current/static/sql-createtable.html#SQL-CREATETABLE-STORAGE-PARAMETERS
/// </remarks>
/// <param name="indexBuilder">The builder for the index being configured.</param>
/// <param name="parameterName">The name of the storage parameter.</param>
/// <param name="parameterValue">The value of the storage parameter.</param>
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
/// <returns><c>true</c> if the index can be configured as with the storage parameter.</returns>
public static bool CanSetStorageParameter(
this IConventionIndexBuilder indexBuilder,
string parameterName,
object? parameterValue, bool fromDataAnnotation = false)
{
Check.NotNull(indexBuilder, nameof(indexBuilder));

return indexBuilder.CanSetAnnotation(NpgsqlAnnotationNames.StorageParameterPrefix + parameterName, parameterValue, fromDataAnnotation);
}

#endregion Storage parameters

#region Sort order (legacy)

/// <summary>
Expand Down Expand Up @@ -1026,4 +1117,4 @@ public static bool CanSetInclude(
=> CanSetIncludeProperties(indexBuilder, propertyNames, fromDataAnnotation);

#endregion Obsolete
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,17 @@ public static class NpgsqlEntityTypeExtensions
#region Storage parameters

/// <summary>
/// Gets all table storage parameters for the table mapped to the entity type.
/// Gets all storage parameters for the table mapped to the entity type.
/// </summary>
public static Dictionary<string, object?> GetStorageParameters(this IReadOnlyEntityType entityType)
=> entityType.GetAnnotations()
.Where(a => a.Name.StartsWith(NpgsqlAnnotationNames.StorageParameterPrefix, StringComparison.Ordinal))
.ToDictionary(
a => a.Name.Substring(NpgsqlAnnotationNames.StorageParameterPrefix.Length),
a => a.Value
);
a => a.Value);

/// <summary>
/// Gets a table storage parameter for the table mapped to the entity type.
/// Gets a storage parameter for the table mapped to the entity type.
/// </summary>
public static string? GetStorageParameter(this IEntityType entityType, string parameterName)
{
Expand All @@ -33,20 +32,17 @@ public static class NpgsqlEntityTypeExtensions
}

/// <summary>
/// Sets a table storage parameter for the table mapped to the entity type.
/// Sets a storage parameter on the table mapped to the entity type.
/// </summary>
public static void SetStorageParameter(
this IMutableEntityType entityType,
string parameterName,
object? parameterValue)
public static void SetStorageParameter(this IMutableEntityType entityType, string parameterName, object? parameterValue)
{
Check.NotEmpty(parameterName, nameof(parameterName));

entityType.SetOrRemoveAnnotation(NpgsqlAnnotationNames.StorageParameterPrefix + parameterName, parameterValue);
}

/// <summary>
/// Sets a table storage parameter for the table mapped to the entity type.
/// Sets a storage parameter on the table mapped to the entity type.
/// </summary>
public static object SetStorageParameter(
this IConventionEntityType entityType,
Expand All @@ -62,7 +58,7 @@ public static object SetStorageParameter(
}

/// <summary>
/// Gets the configuration source fo a table storage parameter for the table mapped to the entity type.
/// Gets the configuration source for a storage parameter for the table mapped to the entity type.
/// </summary>
public static ConfigurationSource? GetStorageParameterConfigurationSource(
this IConventionEntityType index,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,66 @@ public static void SetTsVectorConfig(this IMutableIndex index, string? config)

#endregion ToTsVector

#region Storage parameters

/// <summary>
/// Gets all storage parameters for the index.
/// </summary>
public static Dictionary<string, object?> GetStorageParameters(this IReadOnlyIndex index)
=> index.GetAnnotations()
.Where(a => a.Name.StartsWith(NpgsqlAnnotationNames.StorageParameterPrefix, StringComparison.Ordinal))
.ToDictionary(
a => a.Name.Substring(NpgsqlAnnotationNames.StorageParameterPrefix.Length),
a => a.Value);

/// <summary>
/// Gets a storage parameter for the index.
/// </summary>
public static string? GetStorageParameter(this IIndex index, string parameterName)
{
Check.NotEmpty(parameterName, nameof(parameterName));

return (string?)index[NpgsqlAnnotationNames.StorageParameterPrefix + parameterName];
}

/// <summary>
/// Sets a storage parameter on the index.
/// </summary>
public static void SetStorageParameter(this IMutableIndex index, string parameterName, object? parameterValue)
{
Check.NotEmpty(parameterName, nameof(parameterName));

index.SetOrRemoveAnnotation(NpgsqlAnnotationNames.StorageParameterPrefix + parameterName, parameterValue);
}

/// <summary>
/// Sets a storage parameter on the index.
/// </summary>
public static object SetStorageParameter(
this IConventionIndex index,
string parameterName,
object? parameterValue,
bool fromDataAnnotation = false)
{
Check.NotEmpty(parameterName, nameof(parameterName));

index.SetOrRemoveAnnotation(NpgsqlAnnotationNames.StorageParameterPrefix + parameterName, parameterValue, fromDataAnnotation);

return parameterName;
}

/// <summary>
/// Gets the configuration source for a storage parameter for the table mapped to the entity type.
/// </summary>
public static ConfigurationSource? GetStorageParameterConfigurationSource(this IConventionIndex index, string parameterName)
{
Check.NotEmpty(parameterName, nameof(parameterName));

return index.FindAnnotation(NpgsqlAnnotationNames.StorageParameterPrefix + parameterName)?.GetConfigurationSource();
}

#endregion Storage parameters

#region Sort order (legacy)

/// <summary>
Expand Down Expand Up @@ -469,4 +529,4 @@ public static void SetSortOrder(this IMutableIndex index, IReadOnlyList<SortOrde
=> index.FindAnnotation(NpgsqlAnnotationNames.IndexSortOrder)?.GetConfigurationSource();

#endregion Sort order (legacy)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ protected override void ProcessIndexAnnotations(
annotations.Remove(NpgsqlAnnotationNames.IndexInclude);
annotations.Remove(NpgsqlAnnotationNames.CreatedConcurrently);
annotations.Remove(NpgsqlAnnotationNames.NullsDistinct);

foreach (var annotationName in annotations.Keys.Where(
k => k.StartsWith(NpgsqlAnnotationNames.StorageParameterPrefix, StringComparison.Ordinal)))
{
annotations.Remove(annotationName);
}
}
}
}
6 changes: 6 additions & 0 deletions src/EFCore.PG/Metadata/Internal/NpgsqlAnnotationProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,12 @@ public override IEnumerable<IAnnotation> For(ITableIndex index, bool designTime)
yield return new Annotation(NpgsqlAnnotationNames.NullsDistinct, nullsDistinct);
}

foreach (var storageParamAnnotation in modelIndex.GetAnnotations()
.Where(a => a.Name.StartsWith(NpgsqlAnnotationNames.StorageParameterPrefix, StringComparison.Ordinal)))
{
yield return storageParamAnnotation;
}

// Support legacy annotation for index ordering
if (modelIndex[NpgsqlAnnotationNames.IndexSortOrder] is IReadOnlyList<SortOrder> legacySortOrder)
{
Expand Down
Loading

0 comments on commit f021d6f

Please sign in to comment.