From 30ec1cd56f65ff8a37bc79bda88c59cbe4103100 Mon Sep 17 00:00:00 2001
From: Evdokimov Ilia <ilya.evdokimov@tantorlabs.com>
Date: Thu, 23 Jul 2026 13:50:32 +0300
Subject: [PATCH v2 7/9] Call populate_joinrel_uniquekeys() only when a joinrel
 is first built

populate_joinrel_uniquekeys() was called unconditionally from
make_join_rel() on every call, including when build_join_rel()
returns an already-built joinrel from a different join-order
decomposition of the same relid set (find_join_rel()'s cache hit
path). populate_joinrel_uniquekeys() isn't idempotent -- it
unconditionally appends -- so wide join queries, where the same
relid set is legitimately reached via multiple decompositions,
accumulated duplicate/redundant uniquekeys on the shared joinrel
every time.

Move the call inside build_join_rel()'s "build a new one" branch
instead, alongside the rest of the one-time joinrel setup (tlist,
direct_lateral_relids, has_eclass_joins, etc). It's now called
exactly once per distinct joinrel, the same as everything else
there.
---
 src/backend/optimizer/path/joinrels.c |  2 --
 src/backend/optimizer/util/relnode.c  | 11 +++++++++++
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index fe58f7a928f..443e2dca7c0 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -756,8 +756,6 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
 							 sjinfo, pushed_down_joins,
 							 &restrictlist);
 
-	populate_joinrel_uniquekeys(root, joinrel, rel1, rel2, restrictlist, sjinfo->jointype);
-
 	/*
 	 * If we've already proven this join is empty, we needn't consider any
 	 * more paths for it.
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index beb55a5910a..4fbe266c647 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -946,6 +946,17 @@ build_join_rel(PlannerInfo *root,
 		*restrictlist_ptr = restrictlist;
 	build_joinrel_joinlist(joinrel, outer_rel, inner_rel);
 
+	/*
+	 * Compute the joinrel's uniquekeys here, where we know this is the
+	 * first (and only) time this particular joinrel is being built --
+	 * find_join_rel() above returns the same cached RelOptInfo for every
+	 * later decomposition that produces the same relids, and
+	 * populate_joinrel_uniquekeys() is not idempotent against being called
+	 * more than once on the same joinrel (it unconditionally appends).
+	 */
+	populate_joinrel_uniquekeys(root, joinrel, outer_rel, inner_rel,
+							   restrictlist, sjinfo->jointype);
+
 	/*
 	 * This is also the right place to check whether the joinrel has any
 	 * pending EquivalenceClass joins.
-- 
2.34.1

