Tom Lane <[email protected]> 于2026年10月15日周四 04:56写道:
>
> If we're going to refuse that, I wonder if we couldn't simplify
> match_unique_clauses a great deal: just insist that the two OpExpr
> clauses be equal(), after the ChangeVarNodes substitution on the outer
> clause.  Your proposal already breaks commuted-clause cases, which
> seem like the only justification for all the futzing around with left
> and right operands.  I doubt that detecting that is of any real-world
> value anyway.
>

Attached is v2, following your suggestion to compare the complete
clauses with equal() after the ChangeVarNodes substitution. I also
removed the test added in v1.

I ran the regression tests and found no plan differences. This
suggests the existing tests don't cover cases where matching commuted
restrictions enables self-join elimination.


-- 
Thanks,
Tender Wang
From 17e29bb5a1196af9710041f5673287dc67ec343b Mon Sep 17 00:00:00 2001
From: Tender Wang <[email protected]>
Date: Thu, 15 Oct 2026 10:43:25 +0800
Subject: [PATCH v2] Simplify restriction matching in self-join elimination

Use equal() to compare complete restriction clauses after substituting
the relation ID, instead of comparing their operands separately.  This
ensures that the operator and input collation also match, preventing
incorrect self-join elimination when restrictions have identical
operands but different semantics.

This deliberately forgoes matching commuted clauses in favor of simpler
code.  Remove the checks needed by the operand-matching logic and update
the comments accordingly.
---
 src/backend/optimizer/plan/analyzejoins.c | 40 +++++------------------
 1 file changed, 8 insertions(+), 32 deletions(-)

diff --git a/src/backend/optimizer/plan/analyzejoins.c 
b/src/backend/optimizer/plan/analyzejoins.c
index 3156c658a79..b89a4cb092f 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -1718,10 +1718,12 @@ split_selfjoin_quals(PlannerInfo *root, List 
*joinquals, List **selfjoinquals,
 }
 
 /*
- * Check for a case when uniqueness is at least partly derived from a
- * baserestrictinfo clause. In this case, we have a chance to return only
- * one row (if such clauses on both sides of SJ are equal) or nothing (if they
- * are different).
+ * Check that each base restriction used to prove uniqueness of relid has
+ * a matching restriction in outer->baserestrictinfo, after replacing
+ * relid with outer->relid.
+ *
+ * Require equal() clauses, including the operator and input collation.
+ * We do not attempt to match commuted forms of the clauses.
  */
 static bool
 match_unique_clauses(PlannerInfo *root, RelOptInfo *outer, List *uclauses,
@@ -1730,45 +1732,19 @@ match_unique_clauses(PlannerInfo *root, RelOptInfo 
*outer, List *uclauses,
        foreach_node(RestrictInfo, rinfo, uclauses)
        {
                Expr       *clause;
-               Node       *iclause;
-               Node       *c1;
                bool            matched = false;
 
                Assert(outer->relid > 0 && relid > 0);
 
-               /* Only filters like f(R.x1,...,R.xN) == expr we should 
consider. */
-               Assert(bms_is_empty(rinfo->left_relids) ^
-                          bms_is_empty(rinfo->right_relids));
-
                clause = (Expr *) copyObject(rinfo->clause);
                ChangeVarNodes((Node *) clause, relid, outer->relid, 0);
 
-               iclause = bms_is_empty(rinfo->left_relids) ? 
get_rightop(clause) :
-                       get_leftop(clause);
-               c1 = bms_is_empty(rinfo->left_relids) ? get_leftop(clause) :
-                       get_rightop(clause);
-
                /*
-                * Compare these left and right sides with the corresponding 
sides of
-                * the outer's filters. If no one is detected - return 
immediately.
+                * Look for an identical restriction on the outer relation.
                 */
                foreach_node(RestrictInfo, orinfo, outer->baserestrictinfo)
                {
-                       Node       *oclause;
-                       Node       *c2;
-
-                       if (orinfo->mergeopfamilies == NIL)
-                               /* Don't consider clauses that aren't similar 
to 'F(X)=G(Y)' */
-                               continue;
-
-                       Assert(is_opclause(orinfo->clause));
-
-                       oclause = bms_is_empty(orinfo->left_relids) ?
-                               get_rightop(orinfo->clause) : 
get_leftop(orinfo->clause);
-                       c2 = (bms_is_empty(orinfo->left_relids) ?
-                                 get_leftop(orinfo->clause) : 
get_rightop(orinfo->clause));
-
-                       if (equal(iclause, oclause) && equal(c1, c2))
+                       if (equal(clause, orinfo->clause))
                        {
                                matched = true;
                                break;
-- 
2.43.0

Reply via email to