= ANY with an AND/OR item omits parentheses, returning wrong results
维护者通常 1 天内回复
还没有人认领这个 Issue。
评估
- 难度
- 2/5
- 预计耗时
- 1-3 小时
- 新手友好度
- 82/100
- Issue 类型
- 缺陷
- 描述清晰度
- 描述清楚
- 活跃度
- 活跃
- 技术栈
- csharp, postgresql
调研方向
从 src/EFCore.PG/Query/Internal/NpgsqlQuerySqlGenerator.cs 中约第 890 行的 VisitArrayAny 实现开始,然后检查现有的优先级处理及其测试。使用提供的 Contains 查询重现问题,并验证生成的 SQL 是否将 AND 表达式置于 = ANY 之前进行分组;添加或更新回归测试,以确认查询返回预期的行。
由索引模型根据 Issue 内容生成。
描述
VisitArrayAny writes the item without parentheses. When the item is an AND/OR expression, = binds tighter than AND, so Postgres applies = ANY to the last operand only. The query runs without error and returns the wrong rows.
bool[] availability = [false];
var unavailable = db.Products
.Where(p => availability.Contains(p.IsListed && p.InStock));
| SQL | Result | |
|---|---|---|
| Expected | WHERE (p."IsListed" AND p."InStock") = ANY (@availability) |
Banana, Cherry, Date |
| Actual | WHERE p."IsListed" AND p."InStock" = ANY (@availability) |
Banana |
Seed: Apple listed and in stock, Banana listed and out of stock, Cherry not listed and in stock, Date neither.
Full repro
using Microsoft.EntityFrameworkCore;
await using var db = new ShopContext();
await db.Database.EnsureDeletedAsync();
await db.Database.EnsureCreatedAsync();
db.Products.AddRange(
new Product { Name = "Apple", IsListed = true, InStock = true },
new Product { Name = "Banana", IsListed = true, InStock = false },
new Product { Name = "Cherry", IsListed = false, InStock = true },
new Product { Name = "Date", IsListed = false, InStock = false });
await db.SaveChangesAsync();
bool[] availability = [false];
var query = db.Products
.Where(p => availability.Contains(p.IsListed && p.InStock))
.Select(p => p.Name);
Console.WriteLine(query.ToQueryString());
Console.WriteLine(string.Join(", ", await query.ToListAsync()));
public class ShopContext : DbContext
{
public DbSet<Product> Products => Set<Product>();
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseNpgsql("Host=localhost;Username=postgres;Password=postgres;Database=any_repro");
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = "";
public bool IsListed { get; set; }
public bool InStock { get; set; }
}
Cause: NpgsqlQuerySqlGenerator.cs#L890 calls Visit(expression.Item) with no precedence check. The precedence table already ranks PgAnyExpression at 800 and AndAlso/OrElse at 500 (L1636).
Suggested fix, the same pattern as npgsql/npgsql#3927:
var requiresParentheses = RequiresParentheses(expression, expression.Item);
if (requiresParentheses)
{
Sql.Append("(");
}
Visit(expression.Item);
if (requiresParentheses)
{
Sql.Append(")");
}
Workaround: with a single picked value, filter on the expression or its negation (p.IsListed && p.InStock or !(p.IsListed && p.InStock)) in place of Contains.
Npgsql.EntityFrameworkCore.PostgreSQL 10.0.3, PostgreSQL 17. Happy to send a PR if that helps.
- 主要语言
- C#
- 星标
- 1.8k
- 派生
- 268
- 平均合并
- 2 小时 35 分钟
- 30 天内合并 PR
- 136
环境准备
这个项目没有提供开发容器、Dockerfile 或贡献指南,环境需要你自己搭建:先看它的 README,通用步骤见我们的新手贡献指南。
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
npgsql/efcore.pg 的其他 Issue
-
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 天内回复
-
Array translations: chained `Skip`/`Take` return an empty array, `int.MaxValue` bounds overflow, and `Intersect().Any()` ignores NULL elements可能重新可做 关联的 PR 已关闭且未合并。 未关闭
难度 4/5 3-5 天 新手友好度 30/100
维护者通常 1 天内回复
相似的 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 个 reaction ·
维护者通常 1 天内回复