From: Richard Guo Date: Sat, 11 Apr 2026 07:38:47 +0000 (+0900) Subject: Fix estimate_array_length error with set-operation array coercions X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=77d0e82e58854cfa5e2c4f365a97f16c0d91c394;p=thirdparty%2Fpostgresql.git 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 Author: Tender Wang Reviewed-by: Richard Guo Discussion: https://postgr.es/m/adjW8rfPDkplC7lF@pryzbyj2023 Backpatch-through: 17 --- diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 4160d2d6e24..f2b58ebfe1e 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -2265,6 +2265,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)) { diff --git a/src/test/regress/expected/union.out b/src/test/regress/expected/union.out index 709c85f2294..3a49b354058 100644 --- a/src/test/regress/expected/union.out +++ b/src/test/regress/expected/union.out @@ -1661,3 +1661,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) + diff --git a/src/test/regress/sql/union.sql b/src/test/regress/sql/union.sql index d0c70fafbea..078c858429f 100644 --- a/src/test/regress/sql/union.sql +++ b/src/test/regress/sql/union.sql @@ -664,3 +664,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[];