Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
5 changes: 5 additions & 0 deletions src/EFCore.Abstractions/IndexAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ public bool IsUnique
set => _isUnique = value;
}

/// <summary>
/// A set of values indicating whether each corresponding index column has descending sort order.
/// </summary>
public bool[]? IsDescending { get; set; }

/// <summary>
/// Checks whether <see cref="IsUnique" /> has been explicitly set to a value.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,14 @@ protected virtual void Generate(CreateIndexOperation operation, IndentedStringBu
.Append("unique: true");
}

if (operation.IsDescending is not null)
{
builder
.AppendLine(",")
.Append("descending: ")
.Append(Code.Literal(operation.IsDescending));
}

if (operation.Filter != null)
{
builder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,15 @@ protected virtual void GenerateIndex(
.Append(".IsUnique()");
}

if (index.IsDescending is not null)
{
stringBuilder
.AppendLine()
.Append(".IsDescending(")
.Append(string.Join(", ", index.IsDescending.Select(Code.Literal)))
.Append(')');
}

GenerateIndexAnnotations(indexBuilderName, index, stringBuilder);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,11 @@ private void GenerateIndex(IIndex index)
lines.Add($".{nameof(IndexBuilder.IsUnique)}()");
}

if (index.IsDescending is not null)
{
lines.Add($".{nameof(IndexBuilder.IsDescending)}({string.Join(", ", index.IsDescending.Select(d => _code.Literal(d)))})");
}

GenerateAnnotations(index, annotations, lines);

AppendMultiLineFluentApi(index.DeclaringEntityType, lines);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,11 @@ private void GenerateIndexAttributes(IEntityType entityType)
indexAttribute.AddParameter($"{nameof(IndexAttribute.IsUnique)} = {_code.Literal(index.IsUnique)}");
}

if (index.IsDescending is not null)
{
indexAttribute.AddParameter($"{nameof(IndexAttribute.IsDescending)} = {_code.UnknownLiteral(index.IsDescending)}");
}

_sb.AppendLine(indexAttribute.ToString());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,12 @@ protected virtual EntityTypeBuilder VisitIndexes(EntityTypeBuilder builder, ICol
? builder.HasIndex(propertyNames)
: builder.HasIndex(propertyNames, index.Name);

indexBuilder = indexBuilder.IsUnique(index.IsUnique);
indexBuilder.IsUnique(index.IsUnique);

if (index.IsDescending.Any(desc => desc))
{
indexBuilder.IsDescending(index.IsDescending.ToArray());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep indexBuilder updated; Fluent API doesn't guarantee that old builders remain valid.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering about that - saw some other places where it wasn't updated. May be worth doing a pass to check.

}

if (index.Filter != null)
{
Expand Down
15 changes: 13 additions & 2 deletions src/EFCore.Relational/Metadata/ITableIndex.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ public interface ITableIndex : IAnnotatable
/// </summary>
bool IsUnique { get; }

/// <summary>
/// A set of values indicating whether each corresponding index column has descending sort order.
/// </summary>
IReadOnlyList<bool>? IsDescending { get; }

/// <summary>
/// Gets the expression used as the index filter.
/// </summary>
Expand Down Expand Up @@ -70,8 +75,14 @@ string ToDebugString(MetadataDebugStringOptions options = MetadataDebugStringOpt

builder
.Append(Name)
.Append(' ')
.Append(ColumnBase.Format(Columns));
.Append(" {")
.AppendJoin(
", ",
Enumerable.Range(0, Columns.Count)
.Select(
i =>
$"'{Columns[i].Name}'{(IsDescending is not null && i < IsDescending.Count && IsDescending[i] ? " Desc" : "")}"))
.Append('}');

if (IsUnique)
{
Expand Down
2 changes: 1 addition & 1 deletion src/EFCore.Relational/Metadata/Internal/RelationalModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -952,7 +952,7 @@ private static void PopulateConstraints(Table table)
continue;
}

tableIndex = new TableIndex(name, table, columns, index.IsUnique);
tableIndex = new TableIndex(name, table, columns, index.IsUnique, index.IsDescending);

table.Indexes.Add(name, tableIndex);
}
Expand Down
7 changes: 6 additions & 1 deletion src/EFCore.Relational/Metadata/Internal/TableIndex.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ public TableIndex(
string name,
Table table,
IReadOnlyList<Column> columns,
bool unique)
bool unique,
IReadOnlyList<bool>? isDescending)
{
Name = name;
Table = table;
Columns = columns;
IsUnique = unique;
IsDescending = isDescending;
}

/// <inheritdoc />
Expand Down Expand Up @@ -68,6 +70,9 @@ public override bool IsReadOnly
/// <inheritdoc />
public virtual bool IsUnique { get; }

/// <inheritdoc />
public virtual IReadOnlyList<bool>? IsDescending { get; }

/// <inheritdoc />
public virtual string? Filter
=> MappedIndexes.First().GetFilter(StoreObjectIdentifier.Table(Table.Name, Table.Schema));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1393,6 +1393,11 @@ protected virtual IEnumerable<MigrationOperation> Diff(

private bool IndexStructureEquals(ITableIndex source, ITableIndex target, DiffContext diffContext)
=> source.IsUnique == target.IsUnique
&& (
source.IsDescending is null == target.IsDescending is null
|| source.IsDescending is not null
&& target.IsDescending is not null
&& source.IsDescending.SequenceEqual(target.IsDescending))
&& source.Filter == target.Filter
&& !HasDifferences(source.GetAnnotations(), target.GetAnnotations())
&& source.Columns.Select(p => p.Name).SequenceEqual(
Expand Down
19 changes: 16 additions & 3 deletions src/EFCore.Relational/Migrations/MigrationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -601,21 +601,27 @@ public virtual AlterOperationBuilder<AlterTableOperation> AlterTable(
/// <param name="schema">The schema that contains the table, or <see langword="null" /> to use the default schema.</param>
/// <param name="unique">Indicates whether or not the index enforces uniqueness.</param>
/// <param name="filter">The filter to apply to the index, or <see langword="null" /> for no filter.</param>
/// <param name="descending">
/// A set of values indicating whether each corresponding index column has descending sort order.
/// If <see langword="null" />, all columns will have ascending order.
/// </param>
/// <returns>A builder to allow annotations to be added to the operation.</returns>
public virtual OperationBuilder<CreateIndexOperation> CreateIndex(
string name,
string table,
string column,
string? schema = null,
bool unique = false,
string? filter = null)
string? filter = null,
bool[]? descending = null)
=> CreateIndex(
name,
table,
new[] { Check.NotEmpty(column, nameof(column)) },
schema,
unique,
filter);
filter,
descending);

/// <summary>
/// Builds a <see cref="CreateIndexOperation" /> to create a new composite (multi-column) index.
Expand All @@ -629,14 +635,19 @@ public virtual OperationBuilder<CreateIndexOperation> CreateIndex(
/// <param name="schema">The schema that contains the table, or <see langword="null" /> to use the default schema.</param>
/// <param name="unique">Indicates whether or not the index enforces uniqueness.</param>
/// <param name="filter">The filter to apply to the index, or <see langword="null" /> for no filter.</param>
/// <param name="descending">
/// A set of values indicating whether each corresponding index column has descending sort order.
/// If <see langword="null" />, all columns will have ascending order.
/// </param>
/// <returns>A builder to allow annotations to be added to the operation.</returns>
public virtual OperationBuilder<CreateIndexOperation> CreateIndex(
string name,
string table,
string[] columns,
string? schema = null,
bool unique = false,
string? filter = null)
string? filter = null,
bool[]? descending = null)
{
Check.NotEmpty(name, nameof(name));
Check.NotEmpty(table, nameof(table));
Expand All @@ -649,8 +660,10 @@ public virtual OperationBuilder<CreateIndexOperation> CreateIndex(
Name = name,
Columns = columns,
IsUnique = unique,
IsDescending = descending,
Filter = filter
};

Operations.Add(operation);

return new OperationBuilder<CreateIndexOperation>(operation);
Expand Down
42 changes: 31 additions & 11 deletions src/EFCore.Relational/Migrations/MigrationsSqlGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -428,9 +428,11 @@ protected virtual void Generate(
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name))
.Append(" ON ")
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Table, operation.Schema))
.Append(" (")
.Append(ColumnList(operation.Columns))
.Append(")");
.Append(" (");

GenerateIndexColumnList(operation, model, builder);

builder.Append(")");

IndexOptions(operation, model, builder);

Expand Down Expand Up @@ -1659,23 +1661,41 @@ protected virtual void CheckConstraint(
/// <param name="operation">The operation.</param>
/// <param name="model">The target model which may be <see langword="null" /> if the operations exist without a model.</param>
/// <param name="builder">The command builder to use to add the SQL fragment.</param>
protected virtual void IndexTraits(
MigrationOperation operation,
IModel? model,
MigrationCommandListBuilder builder)
protected virtual void IndexTraits(MigrationOperation operation, IModel? model, MigrationCommandListBuilder builder)
{
}

/// <summary>
/// Returns a SQL fragment for the column list of an index from a <see cref="CreateIndexOperation" />.
/// </summary>
/// <param name="operation">The operation.</param>
/// <param name="model">The target model which may be <see langword="null" /> if the operations exist without a model.</param>
/// <param name="builder">The command builder to use to add the SQL fragment.</param>
protected virtual void GenerateIndexColumnList(CreateIndexOperation operation, IModel? model, MigrationCommandListBuilder builder)
{
for (var i = 0; i < operation.Columns.Length; i++)
{
if (i > 0)
{
builder.Append(", ");
}

builder.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Columns[i]));

if (operation.IsDescending is not null && i < operation.IsDescending.Length && operation.IsDescending[i])
{
builder.Append(" DESC");
}
}
}

/// <summary>
/// Generates a SQL fragment for extras (filter, included columns, options) of an index from a <see cref="CreateIndexOperation" />.
/// </summary>
/// <param name="operation">The operation.</param>
/// <param name="model">The target model which may be <see langword="null" /> if the operations exist without a model.</param>
/// <param name="builder">The command builder to use to add the SQL fragment.</param>
protected virtual void IndexOptions(
CreateIndexOperation operation,
IModel? model,
MigrationCommandListBuilder builder)
protected virtual void IndexOptions(CreateIndexOperation operation, IModel? model, MigrationCommandListBuilder builder)
{
if (!string.IsNullOrEmpty(operation.Filter))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@ namespace Microsoft.EntityFrameworkCore.Migrations.Operations;
[DebuggerDisplay("CREATE INDEX {Name} ON {Table}")]
public class CreateIndexOperation : MigrationOperation, ITableMigrationOperation
{
/// <summary>
/// Indicates whether or not the index should enforce uniqueness.
/// </summary>
public virtual bool IsUnique { get; set; }

/// <summary>
/// The name of the index.
/// </summary>
Expand All @@ -37,6 +32,16 @@ public class CreateIndexOperation : MigrationOperation, ITableMigrationOperation
/// </summary>
public virtual string[] Columns { get; set; } = null!;

/// <summary>
/// Indicates whether or not the index should enforce uniqueness.
/// </summary>
public virtual bool IsUnique { get; set; }

/// <summary>
/// A set of values indicating whether each corresponding index column has descending sort order.
/// </summary>
public virtual bool[]? IsDescending { get; set; }

/// <summary>
/// An expression to use as the index filter.
/// </summary>
Expand All @@ -53,11 +58,12 @@ public static CreateIndexOperation CreateFrom(ITableIndex index)

var operation = new CreateIndexOperation
{
IsUnique = index.IsUnique,
Name = index.Name,
Schema = index.Table.Schema,
Table = index.Table.Name,
Columns = index.Columns.Select(p => p.Name).ToArray(),
IsUnique = index.IsUnique,
IsDescending = index.IsDescending?.ToArray(),
Filter = index.Filter
};
operation.AddAnnotations(index.GetAnnotations());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,15 @@ public class DatabaseIndex : Annotatable
public virtual IList<DatabaseColumn> Columns { get; } = new List<DatabaseColumn>();

/// <summary>
/// Indicates whether or not the index constrains uniqueness.
/// Indicates whether or not the index enforces uniqueness.
/// </summary>
public virtual bool IsUnique { get; set; }

/// <summary>
/// A set of values indicating whether each corresponding index column has descending sort order.
/// </summary>
public virtual IList<bool> IsDescending { get; set; } = new List<bool>();

/// <summary>
/// The filter expression, or <see langword="null" /> if the index has no filter.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -748,10 +748,9 @@ protected override void Generate(

IndexTraits(operation, model, builder);

builder
.Append("(")
.Append(ColumnList(operation.Columns))
.Append(")");
builder.Append("(");
GenerateIndexColumnList(operation, model, builder);
builder.Append(")");
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -932,6 +932,7 @@ private void GetIndexes(DbConnection connection, IReadOnlyList<DatabaseTable> ta
[i].[filter_definition],
[i].[fill_factor],
COL_NAME([ic].[object_id], [ic].[column_id]) AS [column_name],
[ic].[is_descending_key],
[ic].[is_included_column]
FROM [sys].[indexes] AS [i]
JOIN [sys].[tables] AS [t] ON [i].[object_id] = [t].[object_id]
Expand Down Expand Up @@ -1137,6 +1138,8 @@ bool TryGetIndex(
return false;
}

index.IsDescending.Add(dataRecord.GetValueOrDefault<bool>("is_descending_key"));

index.Columns.Add(column);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -458,8 +458,9 @@ private void GetIndexes(DbConnection connection, DatabaseTable table)
using (var command2 = connection.CreateCommand())
{
command2.CommandText = new StringBuilder()
.AppendLine("SELECT \"name\"")
.AppendLine("FROM pragma_index_info(@index)")
.AppendLine("SELECT \"name\", \"desc\"")
.AppendLine("FROM pragma_index_xinfo(@index)")
.AppendLine("WHERE key = 1")
.AppendLine("ORDER BY \"seqno\";")
.ToString();

Expand All @@ -477,6 +478,7 @@ private void GetIndexes(DbConnection connection, DatabaseTable table)
Check.DebugAssert(column != null, "column is null.");

index.Columns.Add(column);
index.IsDescending.Add(reader2.GetBoolean(1));
}
}

Expand Down
Loading