From 823e77c7b27b59603bbefee1a1a52d0217c326cf Mon Sep 17 00:00:00 2001
From: William Bernbaum <wbernbaum@dwdev.com>
Date: Wed, 26 Aug 2026 12:24:09 -0700
Subject: [PATCH v1 08/16] Allow a clause list to be costed under a second join
 type

hide_clause_selectivities() takes the cache off a clause list and returns it; restore_clause_selectivities() puts it back. The saved block carries the list it came from, so restoring it can't hand back the wrong one.
---
 src/backend/optimizer/path/clausesel.c | 72 ++++++++++++++++++++++++++
 src/include/optimizer/optimizer.h      |  4 ++
 src/tools/pgindent/typedefs.list       |  1 +
 3 files changed, 77 insertions(+)

diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c
index 25c4d177ad9..1d46ecc3a9c 100644
--- a/src/backend/optimizer/path/clausesel.c
+++ b/src/backend/optimizer/path/clausesel.c
@@ -975,3 +975,75 @@ clause_selectivity_ext(PlannerInfo *root,
 
 	return s1;
 }
+
+/*
+ * The selectivities taken off a clause list, held together with the list they
+ * came from.
+ */
+struct SavedSelectivities
+{
+	List	   *clauses;
+	Selectivity selec[FLEXIBLE_ARRAY_MEMBER];
+};
+
+/*
+ * hide_clause_selectivities
+ *	  Take the cached selectivities off a clause list and return them.
+ *
+ * A clause holds one cached selectivity for JOIN_INNER and one for whatever
+ * other join type it is examined under, which suffices because a clause belongs
+ * to a single join.  A caller that costs one clause list a second time, under a
+ * join type of its own choosing, has to set the cache aside for the duration
+ * and hand it back with restore_clause_selectivities(), or it will both read
+ * estimates meant for the real join and leave its own behind for that join to
+ * find.
+ *
+ * Hiding a list that is already hidden loses the original selectivities, so a
+ * caller must reach the matching restore before it costs the list again.
+ */
+SavedSelectivities *
+hide_clause_selectivities(List *clauses)
+{
+	SavedSelectivities *saved;
+	int			i = 0;
+	ListCell   *lc;
+
+	saved = (SavedSelectivities *) palloc(offsetof(SavedSelectivities, selec) +
+										  list_length(clauses) * 2 *
+										  sizeof(Selectivity));
+	saved->clauses = clauses;
+
+	foreach(lc, clauses)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+
+		saved->selec[i++] = rinfo->norm_selec;
+		saved->selec[i++] = rinfo->outer_selec;
+		rinfo->norm_selec = -1;
+		rinfo->outer_selec = -1;
+	}
+
+	return saved;
+}
+
+/*
+ * restore_clause_selectivities
+ *	  Put back what hide_clause_selectivities() took off, discarding whatever
+ *	  was cached in the meantime.
+ */
+void
+restore_clause_selectivities(SavedSelectivities *saved)
+{
+	int			i = 0;
+	ListCell   *lc;
+
+	foreach(lc, saved->clauses)
+	{
+		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+
+		rinfo->norm_selec = saved->selec[i++];
+		rinfo->outer_selec = saved->selec[i++];
+	}
+
+	pfree(saved);
+}
diff --git a/src/include/optimizer/optimizer.h b/src/include/optimizer/optimizer.h
index cb6241e2bdd..fa8db31c8eb 100644
--- a/src/include/optimizer/optimizer.h
+++ b/src/include/optimizer/optimizer.h
@@ -44,6 +44,8 @@ typedef struct HeapTupleData *HeapTuple;
 
 /* in path/clausesel.c: */
 
+typedef struct SavedSelectivities SavedSelectivities;
+
 extern Selectivity clause_selectivity(PlannerInfo *root,
 									  Node *clause,
 									  int varRelid,
@@ -66,6 +68,8 @@ extern Selectivity clauselist_selectivity_ext(PlannerInfo *root,
 											  JoinType jointype,
 											  SpecialJoinInfo *sjinfo,
 											  bool use_extended_stats);
+extern SavedSelectivities *hide_clause_selectivities(List *clauses);
+extern void restore_clause_selectivities(SavedSelectivities *saved);
 
 /* in path/costsize.c: */
 
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index cf0ac5b351b..334e27903e8 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2798,6 +2798,7 @@ SYSTEM_INFO
 SampleScan
 SampleScanGetSampleSize_function
 SampleScanState
+SavedSelectivities
 SavedTransactionCharacteristics
 ScalarArrayOpExpr
 ScalarArrayOpExprHashEntry
