Conversions to text: a `char` space is lost, `Convert.ToString(bool)` gives `true`, and `string.Join` with a null separator returns NULL

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

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

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

評価

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

調査の方向性

Start from NpgsqlStringMethodTranslator.cs (lines 217-225, 274-279) and NpgsqlObjectToStringTranslator.cs (102-107) to pad char values with rpad(x,1); in NpgsqlConvertTranslator.cs (36) use a CASE for bool like bool.ToString. Mimic #4001's rpad fix. Run the issue's repro and confirm the generated SQL matches .NET output (space kept, Convert.ToString(bool) gives True, null separator coalesced).

索引モデルが 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

The first repro prints the same output on both lines; the IndexOf one differs by line, as shown. main at 150011d has the same code.

LINQ Row .NET PostgreSQL SQL
string.Join(sep, r.Tags), char sep = ' ' {a,b} a b ab array_to_string(r."Tags", @sep, '')
r.Text.PadRight(4, sep), char sep = ' ' abc abc␠ abc rpad(r."Text", 4, @sep)
r.Space.ToString() char column ' ' " " "" r."Space"::text
r.Text.IndexOf(r.Space), 10.x only a b, ' ' 1 0 strpos(r."Text", r."Space") - 1
Convert.ToString(r.Flag) true True true r."Flag"::text
string.Join(r.Separator, r.Tags) separator NULL ab NULL array_to_string(r."Tags", r."Separator", '')
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 { Tags = ["a", "b"], Space = ' ', Flag = true, Separator = null, Text = "abc" });
await context.SaveChangesAsync();
var row = await context.Rows.AsNoTracking().SingleAsync();
var sep = ' ';

// 1. A space in character(1) is lost when it becomes text
Console.WriteLine($"string.Join(' ' parameter, Tags): .NET {string.Join(sep, row.Tags)}|, database {await context.Rows.Select(r => string.Join(sep, r.Tags)).SingleAsync()}|");
Console.WriteLine($"Text.PadRight(4, ' ' parameter): .NET {row.Text.PadRight(4, sep)}|, database {await context.Rows.Select(r => r.Text.PadRight(4, sep)).SingleAsync()}|");
Console.WriteLine($"Space.ToString(): .NET {row.Space.ToString()}|, database {await context.Rows.Select(r => r.Space.ToString()).SingleAsync()}|");

// 2. bool to text
Console.WriteLine($"Convert.ToString(Flag): .NET {Convert.ToString(row.Flag)}, database {await context.Rows.Select(r => Convert.ToString(r.Flag)).SingleAsync()}");

// 3. string.Join treats a null separator as ""
Console.WriteLine($"string.Join(null separator, Tags): .NET {string.Join(row.Separator, row.Tags)}, database {await context.Rows.Select(r => string.Join(r.Separator, r.Tags)).SingleAsync() ?? "NULL"}");

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

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

public class Row
{
    public int Id { get; set; }
    public string[] Tags { get; set; } = [];
    public char Space { get; set; }
    public bool Flag { get; set; }
    public string? Separator { get; set; }
    public string Text { get; set; } = "";
}

Output (the | marks the end of each value), the same on both lines:

string.Join(' ' parameter, Tags): .NET a b|, database ab|
Text.PadRight(4, ' ' parameter): .NET abc |, database abc|
Space.ToString(): .NET  |, database |
Convert.ToString(Flag): .NET True, database true
string.Join(null separator, Tags): .NET ab, database NULL
Repro (`IndexOf` with a char column, 10.x)
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 { Text = "a b", Space = ' ' });
await context.SaveChangesAsync();
var row = await context.Rows.AsNoTracking().SingleAsync();
Console.WriteLine($"Text.IndexOf(Space): .NET {row.Text.IndexOf(row.Space)}, database {await context.Rows.Select(r => r.Text.IndexOf(r.Space)).SingleAsync()}");

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

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

public class Row
{
    public int Id { get; set; }
    public string Text { get; set; } = "";
    public char Space { get; set; }
}

Output:

.NET 10, 10.0.3:        Text.IndexOf(Space): .NET 1, database 0
.NET 11, 11.0.0-rc.2:   Text.IndexOf(Space): .NET 1, database 1
  • char spaces. char columns and parameters are character(1), and casting character(n) to text strips trailing spaces, so ' ' becomes ''. #4001 handled this for IndexOf on main by padding with rpad(x, 1). Three places still lose it: string.Join (NpgsqlStringMethodTranslator.cs#L274-L279), PadLeft/PadRight (NpgsqlStringMethodTranslator.cs#L217-L225) and ToString() (NpgsqlObjectToStringTranslator.cs#L102-L107). #4001 came with the new char-argument translations and isn't on hotfix/10.0.4, so IndexOf(char column) stays wrong on 10.x.
  • Convert.ToString(bool) is ::text (NpgsqlConvertTranslator.cs#L36). bool.ToString() already uses a CASE that gives True/False.
  • string.Join treats a null separator as empty. array_to_string(..., NULL, '') is NULL, so COALESCE(separator, '') would match.
主要言語
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 を短くまとめたダイジェスト。