]> 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:38:47 +0000 (16:38 +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 4160d2d6e24a8e6b406fa9297e1d3fa8208c6be1..f2b58ebfe1ece7639daf582ef69e353eaac794c4 100644 (file)
@@ -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))
                {
index 709c85f22948acb52feabd3c1cf0ad5de5bb6ecc..3a49b35405837760bc304ff877c9fff5178df93f 100644 (file)
@@ -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)
+
index d0c70fafbeaa40162e3c344e3d664e41f3469542..078c858429f6ef3ad38bc67f872a871e533691d1 100644 (file)
@@ -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[];