From 823e77c7b27b59603bbefee1a1a52d0217c326cf Mon Sep 17 00:00:00 2001 From: William Bernbaum 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