BUG #19752: GROUP BY on a constant and a cast to a length-limited array type crashes the server while planning

From: PG Bug reporting form <noreply(at)postgresql(dot)org>
To: pgsql-bugs(at)lists(dot)postgresql(dot)org
Cc: mertkul669(at)gmail(dot)com
Subject: BUG #19752: GROUP BY on a constant and a cast to a length-limited array type crashes the server while planning
Date: 2026-10-08 18:20:17
Message-ID: 19752-928339cd23b58a33@postgresql.org
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-bugs

The following bug has been logged on the website:

Bug reference: 19752
Logged by: Mert Kul
Email address: mertkul669(at)gmail(dot)com
PostgreSQL version: 18.4
Operating system: Ubuntu
Description:

Hi,
I found a planner crash (SIGSEGV) in PostgreSQL 18 with the following
query:

SELECT ARRAY[]::varchar(500)[]
GROUP BY CAST('true' AS boolean);

Tested on 18.6 (REL_18_STABLE at 1370a7832a). The query works on 17.11.
No tables or data are required, and EXPLAIN alone is enough to reproduce
the crash, so the executor proper is never reached. The planner does,
however, call executor expression-evaluation code while constant-folding
the expression.
I first hit the crash with a LEFT JOIN query, but the FROM clause and the
join are not required.

Reproduction (unpatched 18.6)
ARRAY[]::varchar(500)[] GROUP BY CAST('true' AS boolean) crash
ARRAY[]::char(1)[] GROUP BY CAST('true' AS boolean) crash
ARRAY[]::varchar(500)[] without GROUP BY ok
ARRAY[]::varchar[] GROUP BY ... (no typmod) ok
ARRAY[]::text[] GROUP BY ... ok
ARRAY['a']::varchar(500)[] GROUP BY ... (non-empty array) ok
The crash needs both a constant GROUP BY expression and an array whose
element type requires length coercion, such as varchar(n) or char(n).

Backtrace (18.6, assert-enabled, -O0)
#0 slot_getsomeattrs (slot=0x0, attnum=1) at tuptable.h:361
#1 ExecInterpExpr (...) at execExprInterp.c:664
#2 ExecInterpExprStillValid (...) at execExprInterp.c:2307
#3 ExecEvalExprSwitchContext (...) at executor.h:440
#4 evaluate_expr (expr=..., result_type=1015, result_typmod=504,
result_collation=100) at clauses.c:5082
#5 eval_const_expressions_mutator (node=<ArrayCoerceExpr>, ...)
at clauses.c:3155
#6 expression_tree_mutator_impl (...)
#7 eval_const_expressions_mutator (...) at clauses.c:3797
#8 expression_tree_mutator_impl (...)
#9 eval_const_expressions_mutator (...) at clauses.c:3797
#10 eval_const_expressions (...) at clauses.c:2295
#11 preprocess_expression (..., kind=1) at planner.c:1348
#12 subquery_planner (...) at planner.c:919

Cause
For ARRAY[]::varchar(500)[], the parser builds an ArrayCoerceExpr whose
elemexpr is a per-element template:
varchar(CaseTestExpr, 500, true)
where the final "true" is the internal isExplicit flag.
substitute_grouped_columns_mutator() in parse_agg.c replaces any
subexpression that equal()s a GROUP BY expression with a Var referencing
the RTE_GROUP RTE. It also descends into elemexpr. The GROUP BY
expression is Const(bool, true), and the internal isExplicit flag is also
Const(bool, true), so the flag is wrongly replaced by a Var. (Dumping
elemexpr at the point of failure on 18.4 shows the third argument is a
VAR rather than a CONST.)
eval_const_expressions_mutator() then sees an ArrayCoerceExpr with a
Const argument and no volatile functions in elemexpr, and folds it
through evaluate_expr(). ExecInitExprRec() assumes elemexpr contains no
Var, but the substitution above violates that assumption. The Var is
evaluated without a tuple slot, which dereferences NULL.

Why 17 is not affected
In 17, check_ungrouped_columns_walker() performs the same equal() check,
but a match only stops the descent; the expression tree is not rewritten.
Commit 247dea89f ("Introduce an RTE for the grouping step") made a match
rewrite the tree, without skipping these template fields.
The ArrayCoerceExpr folding code in clauses.c is identical in 17 and 18,
so the relevant change is on the parser side, in parse_agg.c.

Thanks,
Mert Kul

Responses

Browse pgsql-bugs by date

  From Date Subject
Next Message Peter Geoghegan 2026-10-08 18:21:45 Re: Assertion failure in _bt_pagedel (leafblkno == scanblkno) after interrupted VACUUM
Previous Message Rahul 2026-10-08 15:02:26 Re: PostgreSQL 18.6/17.11: standby PANIC on restart after VM truncation