Tom Lane <[email protected]> 于2026年1月1日周四 07:37写道:

> Nicolas Adenis-Lamarre <[email protected]> writes:
> > - detect anti join on "a left join b where x is null" where x is a non
> null
> > var b (b being a rte)
> > this is the object of the attached patched.
>
> This is a perfectly reasonable thing to do, especially now that we've
> built out some infrastructure that would help.  It doesn't look like
> your patch is using that though.  Take a look at commits 904f6a593
> and e2debb643.
>

Yes, after commits 904f6a593 and e2debb643, we have some infrastructure to
use.
I provided a patch to implement this reduction using these infrastructure
codes.
Please check the attached patch.

>
> BTW, it is not a good look for even a draft patch to not bother
> updating adjacent comments that it falsifies, such as this in
> reduce_outer_joins_pass2:
>
>          * See if we can reduce JOIN_LEFT to JOIN_ANTI.  This is the case
> if
>          * the join's own quals are strict for any var that was forced
> null by
>          * higher qual levels.  NOTE: there are other ways that we could
>          * detect an anti-join, in particular if we were to check whether
> Vars
>          * coming from the RHS must be non-null because of table
> constraints.
>          * That seems complicated and expensive though (in particular, one
>          * would have to be wary of lower outer joins). For the moment this
>          * seems sufficient.
>
> In the long run, the comments are as important as the code, if not
> even more so.  Keeping them accurate is not optional.
>

I updated the comments in the attached patch as well.
And I test the regression in the src, all tests pass.
Any thoughts?

-- 
Thanks,
Tender Wang
From 30428c24ad378f5df4082e07f10fd61dda6d4873 Mon Sep 17 00:00:00 2001
From: Tender Wang <[email protected]>
Date: Thu, 1 Jan 2026 13:44:18 +0800
Subject: [PATCH] Reduce JOIN_LEFT TO JOIN_ANTI.

Since we have collected notnull constraints, so we can detect an anti-join,
if we were to check whether Vars coming from the RHS must be non-null because 
of table constraints.
For example,
SELECT ... FROM a LEFT JOIN b ON (a.x = b.y) WHERE b.z IS NULL;
If the b.z has non-null table constraint, we can reduce JOIN_LEFT to JOIN_ANTI.
---
 src/backend/optimizer/prep/prepjointree.c | 64 +++++++++++++++++++++--
 1 file changed, 59 insertions(+), 5 deletions(-)

diff --git a/src/backend/optimizer/prep/prepjointree.c 
b/src/backend/optimizer/prep/prepjointree.c
index c3b726e93e7..4ff27de5a58 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -177,6 +177,8 @@ static Node *find_jointree_node_for_rel(Node *jtnode, int 
relid);
 static nullingrel_info *get_nullingrels(Query *parse);
 static void get_nullingrels_recurse(Node *jtnode, Relids upper_nullingrels,
                                                                        
nullingrel_info *info);
+static bool have_var_is_notnull(PlannerInfo *root, Relids right_rels,
+                                                               List 
*forced_null_vars);
 
 
 /*
@@ -3230,6 +3232,57 @@ reduce_outer_joins(PlannerInfo *root)
        }
 }
 
+/*
+ * have_var_is_notnull
+ * check whether the vars from RHS of join have notnull constraints.
+ */
+static bool
+have_var_is_notnull(PlannerInfo *root, Relids right_rels, List 
*forced_null_vars)
+{
+       ListCell *lc;
+       Bitmapset *cols;
+       int i;
+       int index;
+       RangeTblEntry *rte;
+       Bitmapset *notnullattnums;
+       bool result = false;
+
+       i = 0;
+       foreach(lc, forced_null_vars)
+       {
+               cols = lfirst_node(Bitmapset, lc);
+               if (cols == NULL)
+               {
+                       i++;
+                       continue;
+               }
+               /* Skip if the var doesn't belong to RHS of join */
+               if (!bms_is_member(i, right_rels))
+               {
+                       i++;
+                       continue;
+               }
+               rte = rt_fetch(i, root->parse->rtable);
+               notnullattnums = find_relation_notnullatts(root, rte->relid);
+               index = -1;
+               while ((index = bms_next_member(cols, index)) >= 0)
+               {
+                       AttrNumber attno = index + 
FirstLowInvalidHeapAttributeNumber;
+                       if (bms_is_member(attno, notnullattnums))
+                       {
+                               result = true;
+                               break;
+                       }
+               }
+
+               if (result)
+                       break;
+               i++;
+       }
+
+       return result;
+}
+
 /*
  * reduce_outer_joins_pass1 - phase 1 data collection
  *
@@ -3441,12 +3494,11 @@ reduce_outer_joins_pass2(Node *jtnode,
                /*
                 * See if we can reduce JOIN_LEFT to JOIN_ANTI.  This is the 
case if
                 * the join's own quals are strict for any var that was forced 
null by
-                * higher qual levels.  NOTE: there are other ways that we could
-                * detect an anti-join, in particular if we were to check 
whether Vars
+                * higher qual levels.  Since we have collected notnull 
constraints, so
+                * we can detect an anti-join, if we were to check whether Vars
                 * coming from the RHS must be non-null because of table 
constraints.
-                * That seems complicated and expensive though (in particular, 
one
-                * would have to be wary of lower outer joins). For the moment 
this
-                * seems sufficient.
+                * For example, SELECT ... FROM a LEFT JOIN b ON (a.x = b.y) 
WHERE b.z IS NULL;
+                * if the b.z had non-null table constraint, we can reduce 
JOIN_LEFT to JOIN_ANTI.
                 */
                if (jointype == JOIN_LEFT)
                {
@@ -3464,6 +3516,8 @@ reduce_outer_joins_pass2(Node *jtnode,
                        overlap = mbms_overlap_sets(nonnullable_vars, 
forced_null_vars);
                        if (bms_overlap(overlap, right_state->relids))
                                jointype = JOIN_ANTI;
+                       else if (have_var_is_notnull(root, right_state->relids, 
forced_null_vars))
+                               jointype = JOIN_ANTI;
                }
 
                /*
-- 
2.34.1

Reply via email to