Fix estimate_array_length error with set-operation array coercions

When a nested set operation's output type doesn't match the parent's
expected type, recurse_set_operations builds a projection target list
using generate_setop_tlist with varno 0.  If the required type
coercion involves an ArrayCoerceExpr, estimate_array_length could be
called on such a Var, and would pass it to examine_variable, which
errors in find_base_rel because varno 0 has no valid relation entry.

Fix by skipping the statistics lookup for Vars with varno 0.

Bug introduced by commit 9391f7152.  Back-patch to v17, where
estimate_array_length was taught to use statistics.

Reported-by: Justin Pryzby <pryzby@telsasoft.com>
Author: Tender Wang <tndrwang@gmail.com>
Reviewed-by: Richard Guo <guofenglinux@gmail.com>
Discussion: https://postgr.es/m/adjW8rfPDkplC7lF@pryzbyj2023
Backpatch-through: 17
This commit is contained in:
Richard Guo 2026-04-11 16:38:47 +09:00
parent c05c3baf16
commit 93ed187201
3 changed files with 33 additions and 0 deletions

View file

@ -2165,6 +2165,18 @@ estimate_array_length(PlannerInfo *root, Node *arrayexpr)
AttStatsSlot sslot;
double nelem = 0;
/*
* Skip calling examine_variable for Var with varno 0, which has no
* valid relation entry and would error in find_base_rel. Such a Var
* can appear when a nested set operation's output type doesn't match
* the parent's expected type, because recurse_set_operations builds a
* projection target list using generate_setop_tlist with varno 0, and
* if the required type coercion involves an ArrayCoerceExpr, we can
* be called on that Var.
*/
if (IsA(arrayexpr, Var) && ((Var *) arrayexpr)->varno == 0)
return 10; /* default guess, should match scalararraysel */
examine_variable(root, arrayexpr, 0, &vardata);
if (HeapTupleIsValid(vardata.statsTuple))
{

View file

@ -1489,3 +1489,20 @@ on true limit 1;
-> Result
(6 rows)
-- Test handling of Vars with varno 0 in estimate_array_length
explain (verbose, costs off)
select null::int[] union all select null::int[] union all select null::bigint[];
QUERY PLAN
---------------------------------------------
Append
-> Result
Output: (NULL::integer[])
-> Append
-> Result
Output: NULL::integer[]
-> Result
Output: NULL::integer[]
-> Result
Output: NULL::bigint[]
(10 rows)

View file

@ -577,3 +577,7 @@ select * from tenk1 t1
left join lateral
(select t1.tenthous from tenk2 t2 union all (values(1)))
on true limit 1;
-- Test handling of Vars with varno 0 in estimate_array_length
explain (verbose, costs off)
select null::int[] union all select null::int[] union all select null::bigint[];