Array translations: chained `Skip`/`Take` return an empty array, `int.MaxValue` bounds overflow, and `Intersect().Any()` ignores NULL elements

Offen
#4,098 0 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Maintainer antworten meist innerhalb von 1 Tag

Dieses Issue hat noch niemand übernommen.

  • #4100 von @CaffeinatedCoder — ohne Merge geschlossen

Bewertung

Schwierigkeit
4/5
Geschätzter Aufwand
3-5 Tage
Anfängerfreundlichkeit
30/100
Issue-Typ
Bug
Klarheit
Klar beschrieben
Aktivitätsstatus
Veraltet
Tech-Stack
csharp, postgresql
Bereich
database

Rechercherichtung

Start from src/EFCore.PG/Query/Internal/NpgsqlQueryableMethodTranslatingExpressionVisitor.cs, reading lines 1031-1073 (Skip), 1094-1108 (Take), 1544-1547 (bounds) and 665-668 (Intersect). Run the C# repro in the issue to see the three mismatches. Done means generated SQL uses parenthesized slices, checked bounds, and NULL-safe Intersect so database counts match .NET.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Beschreibung

Environment
PostgreSQL 18.6 (postgres:18), session TimeZone Etc/UTC
.NET 10 runtime 10.0.11, Npgsql.EntityFrameworkCore.PostgreSQL 10.0.3, EF Core 10.0.4
.NET 11 runtime 11.0.0-rc.2, Npgsql.EntityFrameworkCore.PostgreSQL 11.0.0-rc.2, EF Core 11.0.0-rc.2
Also Npgsql 10.0.3 on both lines, macOS arm64

Both lines print the same output. main at 150011d has the same code.

LINQ Row .NET PostgreSQL SQL
r.Ints.Take(2).Skip(1).Count() {1,2,3} 1 0 cardinality(r."Ints"[:2][2:])
r.Ints.Skip(int.MaxValue).Count() {1,2,3} 0 3 cardinality(r."Ints"[-2147483648:])
r.A.Intersect(r.B).Any() {1,NULL,3}, {NULL,5} true false r."A" && r."B"
Repro
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

await using var context = new TestContext();
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();

context.Rows.Add(new Row { Ints = [1, 2, 3], A = [1, null, 3], B = [null, 5] });
await context.SaveChangesAsync();
var row = await context.Rows.AsNoTracking().SingleAsync();

// 1. a[:2][2:] is a two-dimensional slice of a one-dimensional array in PostgreSQL
Console.WriteLine($"Ints.Take(2).Skip(1).Count(): .NET {row.Ints.Take(2).Skip(1).Count()}, database {await context.Rows.Select(r => r.Ints.Take(2).Skip(1).Count()).SingleAsync()}");

// 2. int.MaxValue + 1 overflows into the slice bound
Console.WriteLine($"Ints.Skip(int.MaxValue).Count(): .NET {row.Ints.Skip(int.MaxValue).Count()}, database {await context.Rows.Select(r => r.Ints.Skip(int.MaxValue).Count()).SingleAsync()}");

// 3. && never matches NULL elements; Intersect does
Console.WriteLine($"A.Intersect(B).Any() with a NULL in both: .NET {row.A.Intersect(row.B).Any()}, database {await context.Rows.Select(r => r.A.Intersect(r.B).Any()).SingleAsync()}");

public class TestContext : DbContext
{
    public DbSet<Row> Rows => Set<Row>();

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .UseNpgsql(Environment.GetEnvironmentVariable("Test__Npgsql__DefaultConnection"))
            .LogTo(Console.WriteLine, LogLevel.Information)
            .EnableSensitiveDataLogging();
}

public class Row
{
    public int Id { get; set; }
    public int[] Ints { get; set; } = [];
    public int?[] A { get; set; } = [];
    public int?[] B { get; set; } = [];
}

Output, the same on both lines:

Ints.Take(2).Skip(1).Count(): .NET 1, database 0
Ints.Skip(int.MaxValue).Count(): .NET 0, database 3
A.Intersect(B).Any() with a NULL in both: .NET True, database False
Vorherrschende Sprache
C#
Sterne
1.8k
Forks
268
Ø Merge
2 Std. 35 Min.
Gemergte PRs (30 T.)
136

Entwicklungsumgebung

Dieses Projekt bietet weder Dev-Container noch Dockerfile noch Beitragsleitfaden – die Einrichtung liegt bei Ihnen. Beginnen Sie mit der README; die allgemeinen Schritte stehen in unserem Leitfaden für den ersten Beitrag.

Erste Schritte

  1. Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
  3. Forken Sie das Repository und arbeiten Sie in einem Branch.
  4. Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.

Mehr aus npgsql/efcore.pg

Alle Issues in npgsql/efcore.pg

Ähnliche Issues

Weitere Issues zu C#

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.