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

オープン
#4,098 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

まだ誰も着手していません。

  • #4100 @CaffeinatedCoder による — マージされずにクローズ

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
30/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
停滞
技術スタック
csharp, postgresql
領域
database

調査の方向性

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.

索引モデルが issue の本文から書いたものです。

説明

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
主要言語
C#
スター
1.8k
フォーク
268
平均マージ
2時間 35分
マージ済み PR(30日)
136

環境構築

このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

npgsql/efcore.pg のほかの issue

npgsql/efcore.pg の issue をすべて見る

似ている issue

C# の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。