Regex translation: `Regex.Replace` substitutions are passed through, `Singleline | Multiline` gets the wrong flag, and `[^x]` never matches a newline
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 3/5
- 見積もり時間
- 1〜2日
- 初心者へのやさしさ
- 55/100
調査の方向性
Start in src/EFCore.PG/Query/ExpressionTranslators/Internal/NpgsqlRegexTranslator.cs (lines 111-119, 166-170) and src/EFCore.PG/Query/Internal/NpgsqlQuerySqlGenerator.cs (lines 1022-1030) to see how regex flags and substitutions are emitted. Run the repro DbContext from the issue to observe mismatched SQL. Done means replacements use \1-style substitutions and the w flag for Singleline|Multiline, or a docs note covers gaps.
索引モデルが 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 | Input | .NET | PostgreSQL | SQL |
|---|---|---|---|---|
Regex.Replace(r.Text, "(a)", "[$1]") |
abc |
[a]bc |
[$1]bc |
regexp_replace(r."Text", '(a)', '[$1]', 'p') |
Regex.IsMatch(r.Text, "a.b", RegexOptions.Singleline | RegexOptions.Multiline) |
a\nb |
match | no match | r."Text" ~ '(?n)a.b' |
Regex.IsMatch(r.Text, "a[^x]b") |
a\nb |
match | no match | r."Text" ~ '(?p)a[^x]b' |
Repro
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System.Text.RegularExpressions;
await using var context = new TestContext();
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();
context.Rows.AddRange(new Row { Text = "abc" }, new Row { Text = "a\nb" });
await context.SaveChangesAsync();
var rows = await context.Rows.AsNoTracking().OrderBy(r => r.Id).ToListAsync();
// 1. .NET substitution syntax is passed through; PostgreSQL only knows \1..\9 and \&
Console.WriteLine($"Regex.Replace(\"abc\", \"(a)\", \"[$1]\"): .NET {Regex.Replace(rows[0].Text, "(a)", "[$1]")}, database {await context.Rows.Where(r => r.Id == 1).Select(r => Regex.Replace(r.Text, "(a)", "[$1]")).SingleAsync()}");
// 2. Singleline makes . match \n, also together with Multiline
Console.WriteLine($"IsMatch(\"a\\nb\", \"a.b\", Singleline | Multiline): .NET {rows.Count(r => Regex.IsMatch(r.Text, "a.b", RegexOptions.Singleline | RegexOptions.Multiline))}, database {await context.Rows.CountAsync(r => Regex.IsMatch(r.Text, "a.b", RegexOptions.Singleline | RegexOptions.Multiline))}");
// 3. A negated bracket matches \n in .NET
Console.WriteLine($"IsMatch(\"a\\nb\", \"a[^x]b\"): .NET {rows.Count(r => Regex.IsMatch(r.Text, "a[^x]b"))}, database {await context.Rows.CountAsync(r => Regex.IsMatch(r.Text, "a[^x]b"))}");
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; } = "";
}
Output, the same on both lines:
Regex.Replace("abc", "(a)", "[$1]"): .NET [a]bc, database [$1]bc
IsMatch("a\nb", "a.b", Singleline | Multiline): .NET 1, database 0
IsMatch("a\nb", "a[^x]b"): .NET 1, database 0
- Substitutions are passed through unchanged (NpgsqlRegexTranslator.cs#L111-L119). PostgreSQL expands
\1–\9and\&. .NET uses$1,${name},$&and$$, and treats a backslash literally. So$1comes out as$1, and\1means different things on the two sides. A constant replacement could be rewritten; a column replacement can't easily be. Singleline | Multilinerendersnbecause the Multiline check comes first (NpgsqlQuerySqlGenerator.cs#L1022-L1030, NpgsqlRegexTranslator.cs#L166-L170). Withn,.doesn't match a newline. .NET's Singleline with Multiline corresponds tow:^and$per line, and.matches newlines.- The default
pflag, which is there so that.doesn't match a newline, also stops a negated bracket like[^x]from matching one. .NET's default lets[^x]match\n. There may be no flag combination that matches .NET exactly, so a docs note may be the realistic fix here. The same goes for other dialect differences, for example\b, which is a backspace in PostgreSQL's ARE (a word boundary there is\y).
Regex.Replace on 10.0.3 and 11.0.0-rc.2 still uses 'p' without g. That is already fixed on main and in hotfix/10.0.4 (#4035).
- 主要言語
- C#
- スター
- 1.8k
- フォーク
- 268
- 平均マージ
- 2時間 35分
- マージ済み PR(30日)
- 136
環境構築
このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
npgsql/efcore.pg のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
メンテナーはふだん 1 日以内に返信
-
NTS: the M ordinate is dropped from geometry SQL literals and from geometries stored in JSON対応中かも @Ahmed7991 が 5 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 85/100
npgsql/efcore.pg#4093 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
`TimeSpan`/`Duration` component members: negative values are floored, and intervals computed in SQL are read field by field対応中かも @jeremy-morren が 11 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
メンテナーはふだん 1 日以内に返信
-
Numeric storage parameters are formatted with the current culture, so migrations emit 0,01 instead of 0.01対応中かも @Sorbitol が 12 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 30/100
メンテナーはふだん 1 日以内に返信
-
`Contains` on a JSON primitive collection compares JSON text, so `DateTime` and `TimeOnly` items never match対応中かも @Mr444-dev が 7 日前に担当しました。 オープン
難易度 4/5 3〜5日 初心者へのやさしさ 45/100
npgsql/efcore.pg#4099 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
npgsql/efcore.pg の issue をすべて見る
似ている issue
-
Issue-Bug Needs-Triage
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
メンテナーはふだん 2 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 66/100
JiaFeiMiao-K-Cat/DiscImageStudio#281 ·
メンテナーはふだん 1 日以内に返信
-
area/access-management Frontend onboarding candidate :handshake:
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
Altinn/altinn-auth#4941 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
aws/aws-sdk-net#4586 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
Kryptos-FR/MarkView.Avalonia#269 · リアクション 1 件 ·
メンテナーはふだん 1 日以内に返信