From 41788ab6a9c2fad780d4ad917220ef10763ed3b1 Mon Sep 17 00:00:00 2001
From: Evdokimov Ilia <ilya.evdokimov@tantorlabs.com>
Date: Thu, 23 Jul 2026 13:33:24 +0300
Subject: [PATCH v2 4/9] Guard var_is_nullable() against unpopulated
 simple_rel_array slots

var_is_nullable() dereferenced root->simple_rel_array[var->varno]
unconditionally. That slot isn't always populated for the given
root: var->varno can be a special varno (OUTER_VAR/INNER_VAR), or a
plain varno for which this particular root never built a RelOptInfo
-- e.g. a Var coming from a set operation's own targetlist, whose
member queries are planned via their own separate subroots.

This crashed on copyselect.sql's UNION-in-a-subquery case once the
NOT NULL check added by the previous commit started exercising this
function from a new call site:

    COPY (SELECT * FROM (SELECT t FROM test1 WHERE id = 1
          UNION SELECT * FROM v_test1 ORDER BY 1) t1) TO STDOUT;

Be conservative and treat the Var as nullable when its varno doesn't
resolve to a real, populated RelOptInfo in this root, rather than
dereferencing a NULL/out-of-range pointer.
---
 src/backend/optimizer/path/uniquekey.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/src/backend/optimizer/path/uniquekey.c b/src/backend/optimizer/path/uniquekey.c
index 3974c624799..063fc15530c 100644
--- a/src/backend/optimizer/path/uniquekey.c
+++ b/src/backend/optimizer/path/uniquekey.c
@@ -428,6 +428,20 @@ var_is_nullable(PlannerInfo *root, Var *var, RelOptInfo *rel)
 	if (bms_overlap(var->varnullingrels, rel->relids))
 		return true;
 
+	/*
+	 * varno might not index a populated slot in this root's
+	 * simple_rel_array -- e.g. a special varno (OUTER_VAR/INNER_VAR), or a
+	 * plain varno for which no RelOptInfo was ever built in this
+	 * particular root (this happens for Vars coming from a set-operation's
+	 * targetlist, whose member queries are planned via their own separate
+	 * subroots). Be conservative and treat the var as nullable rather than
+	 * dereferencing a NULL/out-of-range RelOptInfo pointer.
+	 */
+	if (IS_SPECIAL_VARNO(var->varno) ||
+		var->varno >= root->simple_rel_array_size ||
+		root->simple_rel_array[var->varno] == NULL)
+		return true;
+
 	/* check if the user data has the NULL values. */
 	base_rel = root->simple_rel_array[var->varno];
 	return !bms_is_member(var->varattno - FirstLowInvalidHeapAttributeNumber, base_rel->notnullattrs);
-- 
2.34.1

