]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix estimate_array_length error with set-operation array coercions
authorRichard Guo <rguo@postgresql.org>
Sat, 11 Apr 2026 07:38:47 +0000 (16:38 +0900)
committerRichard Guo <rguo@postgresql.org>
Sat, 11 Apr 2026 07:41:58 +0000 (16:41 +0900)
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

src/backend/utils/adt/selfuncs.c
src/test/regress/expected/union.out
src/test/regress/sql/union.sql

index d5468edcf311fbce705e260f73d097ddb41de87d..2c1daefc0c0a0f0106f3db154a250e47c3b0233b 100644 (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))
                {
index 0fd0e1c38b3d7c1836ef3e63db65c025ba8b8bf7..882b075b9a1f7f337afa92d73610618a98e66d34 100644 (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)
+
index f8826514e42a6bda17700193512ce0cb690d1072..c52ca012b5c3973ed9ce09c717f2a1503e435a01 100644 (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[];