]> 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:40:34 +0000 (16:40 +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 2719d8babe97dbaf1d495c8046f55007a43f4d68..c9ff71844e70ef33fc82ab4eb01f860154f2b676 100644 (file)
@@ -2172,6 +2172,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 96962817ed45adc4e97c5e6880b514c15b7d5348..984db95608ca538f96a370b1c0a1296f902a20c6 100644 (file)
@@ -1500,3 +1500,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 13700a6bfc4e918b951d6829e36b970146e62dca..bc50bc40ded5259ac6395e0a3d899716f58600b2 100644 (file)
@@ -592,3 +592,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[];