Skip to content

Commit 6731019

Browse files
authored
Fix index creation when both collation and operators are specified (#3028)
Fixes #3027
1 parent f9e3428 commit 6731019

File tree

3 files changed

+58
-34
lines changed

3 files changed

+58
-34
lines changed

src/EFCore.PG/Migrations/NpgsqlMigrationsSqlGenerator.cs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2051,20 +2051,24 @@ private string DelimitIdentifier(string name, string? schema)
20512051

20522052
private string IndexColumnList(IndexColumn[] columns, string? method)
20532053
{
2054-
var isFirst = true;
20552054
var builder = new StringBuilder();
20562055

20572056
for (var i = 0; i < columns.Length; i++)
20582057
{
2059-
if (!isFirst)
2058+
var column = columns[i];
2059+
2060+
if (i > 0)
20602061
{
20612062
builder.Append(", ");
20622063
}
20632064

2064-
var column = columns[i];
2065-
20662065
builder.Append(DelimitIdentifier(column.Name));
20672066

2067+
if (!string.IsNullOrEmpty(column.Collation))
2068+
{
2069+
builder.Append(" COLLATE ").Append(DelimitIdentifier(column.Collation));
2070+
}
2071+
20682072
if (!string.IsNullOrEmpty(column.Operator))
20692073
{
20702074
var delimitedOperator = TryParseSchema(column.Operator, out var name, out var schema)
@@ -2074,11 +2078,6 @@ private string IndexColumnList(IndexColumn[] columns, string? method)
20742078
builder.Append(" ").Append(delimitedOperator);
20752079
}
20762080

2077-
if (!string.IsNullOrEmpty(column.Collation))
2078-
{
2079-
builder.Append(" COLLATE ").Append(DelimitIdentifier(column.Collation));
2080-
}
2081-
20822081
// Of the built-in access methods, only btree (the default) supports
20832082
// sorting, thus we only want to emit sort options for btree indexes.
20842083
if (method is null or "btree")
@@ -2105,8 +2104,6 @@ private string IndexColumnList(IndexColumn[] columns, string? method)
21052104
}
21062105
}
21072106
}
2108-
2109-
isFirst = false;
21102107
}
21112108

21122109
return builder.ToString();

test/EFCore.PG.FunctionalTests/Migrations/MigrationsNpgsqlTest.cs

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2235,9 +2235,56 @@ await Test(
22352235
@"CREATE INDEX ""IX_People_FirstName_LastName"" ON ""People"" (""FirstName"" text_pattern_ops, ""LastName"");");
22362236
}
22372237

2238-
// Index collation: which collations are available on a given PostgreSQL varies (e.g. Linux vs. Windows),
2239-
// so we test support for this on the generated SQL only, in NpgsqlMigrationSqlGeneratorTest, and not against
2240-
// the database here.
2238+
[Fact]
2239+
public virtual async Task Create_index_with_collation()
2240+
{
2241+
await Test(
2242+
builder =>
2243+
{
2244+
builder.Entity("People", e => e.Property<string>("Name"));
2245+
builder.HasCollation("some_collation", locale: "POSIX", provider: "libc");
2246+
},
2247+
_ => { },
2248+
builder => builder.Entity("People").HasIndex("Name").UseCollation("some_collation"),
2249+
model =>
2250+
{
2251+
var table = Assert.Single(model.Tables);
2252+
var index = Assert.Single(table.Indexes);
2253+
Assert.Equal("some_collation", Assert.Single((IReadOnlyList<string>)index[RelationalAnnotationNames.Collation]!));
2254+
});
2255+
2256+
AssertSql(
2257+
"""
2258+
CREATE INDEX "IX_People_Name" ON "People" ("Name" COLLATE some_collation);
2259+
""");
2260+
}
2261+
2262+
[Fact] // #3027
2263+
public virtual async Task Create_index_with_collation_and_operators()
2264+
{
2265+
await Test(
2266+
builder =>
2267+
{
2268+
builder.Entity("People", e => e.Property<string>("Name"));
2269+
builder.HasCollation("some_collation", locale: "POSIX", provider: "libc");
2270+
},
2271+
_ => { },
2272+
builder => builder.Entity("People").HasIndex("Name")
2273+
.UseCollation("some_collation")
2274+
.HasOperators("text_pattern_ops"),
2275+
model =>
2276+
{
2277+
var table = Assert.Single(model.Tables);
2278+
var index = Assert.Single(table.Indexes);
2279+
Assert.Equal("text_pattern_ops", Assert.Single((IReadOnlyList<string>)index[NpgsqlAnnotationNames.IndexOperators]!));
2280+
Assert.Equal("some_collation", Assert.Single((IReadOnlyList<string>)index[RelationalAnnotationNames.Collation]!));
2281+
});
2282+
2283+
AssertSql(
2284+
"""
2285+
CREATE INDEX "IX_People_Name" ON "People" ("Name" COLLATE some_collation text_pattern_ops);
2286+
""");
2287+
}
22412288

22422289
[Fact]
22432290
public virtual async Task Create_index_with_null_sort_order()

test/EFCore.PG.FunctionalTests/Migrations/NpgsqlMigrationsSqlGeneratorTest.cs

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -473,26 +473,6 @@ public override void Sequence_restart_operation(long? startsAt)
473473
: """ALTER SEQUENCE dbo."TestRestartSequenceOperation" RESTART;""");
474474
}
475475

476-
// Which index collations are available on a given PostgreSQL varies (e.g. Linux vs. Windows)
477-
// so we test support for this on the generated SQL only, and not against the database in MigrationsNpgsqlTest.
478-
[Fact]
479-
public void CreateIndexOperation_collation()
480-
{
481-
Generate(
482-
new CreateIndexOperation
483-
{
484-
Name = "IX_People_Name",
485-
Table = "People",
486-
Schema = "dbo",
487-
Columns = new[] { "FirstName", "LastName" },
488-
[RelationalAnnotationNames.Collation] = new[] { null, "de_DE" }
489-
});
490-
491-
AssertSql(
492-
@"CREATE INDEX ""IX_People_Name"" ON dbo.""People"" (""FirstName"", ""LastName"" COLLATE ""de_DE"");
493-
");
494-
}
495-
496476
[Theory]
497477
[InlineData(MigrationsSqlGenerationOptions.Default)]
498478
[InlineData(MigrationsSqlGenerationOptions.Idempotent)]

0 commit comments

Comments
 (0)