On Tue, Aug 18, 2026 at 9:54 AM Tender Wang <[email protected]> wrote: > On master, the qual is pushed below the DISTINCT: > > ```text > Unique > -> Sort > Sort Key: cit.t > -> Seq Scan on cit > Filter: (CASE (t)::text WHEN 'A'::text THEN 1 ELSE 0 END = 1) > ``` > > This is suspicious because DISTINCT compares `t` using citext > equality, under which `'a'` and `'A'` are equal, while the CASE > expression casts `t` to text and therefore distinguishes them.
Thanks for the report. I looked into it and I think the root cause is that grouping_conflict_walker() treats the arg of a simple CASE as an operand of each WHEN comparison, but only applied the collation half of the direct-operand check, on the assumption that the WHEN operator is always the type-default "=" and thus matches the grouping eqop. That is not true once the arg is relabeled. In your case with "CASE t::text WHEN 'A'", the WHEN compares with texteq while the grouping uses citext_eq, so the qual should be rejected just as "t::text = 'A'" already is. I reviewed your patch. IIUC, it fixes this by adding an equality_ops_are_compatible() check per WHEN inside the CaseExpr branch. That works for the reported query, but I'd rather not go that way, for a few reasons. It duplicates the operand check that grouping_check_operand already implements, so the two would have to be kept in sync. It assumes each WHEN condition is a bare OpExpr (the Assert), which the parser does not guarantee. It also skips the op_is_safe_index_member() gate, which is what makes the opfamily test meaningful for the direct-operand form. More generally, the problem is that the CaseExpr branch re-implements how a direct operand is checked, and does so incompletely. I think it'd be better to avoid this duplication. So I'd like to take the approach used elsewhere in planner for the same placeholder: while walking the WHEN conditions, the walker binds a Var arg in the context and resolves each CaseTestExpr to it. The Var is then checked as each WHEN uses it. This is how eval_const_expressions() handles the CaseTestExpr nodes. Attached is the patch doing that. - Richard
v2-0001-Fix-qual-pushdown-past-grouping-through-simple-CA.patch
Description: Binary data
