Improve outer-join-deduction logic to be able to propagate equalities
authorTom Lane
Sun, 3 Jul 2005 18:26:32 +0000 (18:26 +0000)
committerTom Lane
Sun, 3 Jul 2005 18:26:32 +0000 (18:26 +0000)
through multiple join clauses.

src/backend/optimizer/path/pathkeys.c

index 389d89f0d8da80682dc3b6f35042948513ee38c9..26ccfd9297edc313e024f5b9594091b16719e8ee 100644 (file)
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/path/pathkeys.c,v 1.69 2005/07/02 23:00:40 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/path/pathkeys.c,v 1.70 2005/07/03 18:26:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -35,6 +35,10 @@ static PathKeyItem *makePathKeyItem(Node *key, Oid sortop, bool checkType);
 static void generate_outer_join_implications(PlannerInfo *root,
                                             List *equi_key_set,
                                             Relids *relids);
+static void sub_generate_join_implications(PlannerInfo *root,
+                                          List *equi_key_set, Relids *relids,
+                                          Node *item1, Oid sortop1,
+                                          Relids item1_relids);
 static void process_implied_const_eq(PlannerInfo *root,
                                     List *equi_key_set, Relids *relids,
                                     Node *item1, Oid sortop1,
@@ -250,65 +254,65 @@ generate_implied_equalities(PlannerInfo *root)
            i1++;
        }
 
-       /*
-        * If we have constant(s) and outer joins, try to propagate the
-        * constants through outer-join quals.
-        */
-       if (have_consts && root->hasOuterJoins)
-           generate_outer_join_implications(root, curset, relids);
-
-       /*
-        * A set containing only two items cannot imply any equalities
-        * beyond the one that created the set, so we can skip it.
-        */
-       if (nitems < 3)
-           continue;
-
        /*
         * Match each item in the set with all that appear after it (it's
         * sufficient to generate A=B, need not process B=A too).
+        *
+        * A set containing only two items cannot imply any equalities
+        * beyond the one that created the set, so we can skip this
+        * processing in that case.
         */
-       i1 = 0;
-       foreach(ptr1, curset)
+       if (nitems >= 3)
        {
-           PathKeyItem *item1 = (PathKeyItem *) lfirst(ptr1);
-           bool        i1_is_variable = !bms_is_empty(relids[i1]);
-           ListCell   *ptr2;
-           int         i2 = i1 + 1;
-
-           for_each_cell(ptr2, lnext(ptr1))
+           i1 = 0;
+           foreach(ptr1, curset)
            {
-               PathKeyItem *item2 = (PathKeyItem *) lfirst(ptr2);
-               bool        i2_is_variable = !bms_is_empty(relids[i2]);
+               PathKeyItem *item1 = (PathKeyItem *) lfirst(ptr1);
+               bool        i1_is_variable = !bms_is_empty(relids[i1]);
+               ListCell   *ptr2;
+               int         i2 = i1 + 1;
 
-               /*
-                * If it's "const = const" then just ignore it altogether.
-                * There is no place in the restrictinfo structure to
-                * store it.  (If the two consts are in fact unequal, then
-                * propagating the comparison to Vars will cause us to
-                * produce zero rows out, as expected.)
-                */
-               if (i1_is_variable || i2_is_variable)
+               for_each_cell(ptr2, lnext(ptr1))
                {
+                   PathKeyItem *item2 = (PathKeyItem *) lfirst(ptr2);
+                   bool        i2_is_variable = !bms_is_empty(relids[i2]);
+
                    /*
-                    * Tell process_implied_equality to delete the clause,
-                    * not add it, if it's "var = var" and we have
-                    * constants present in the list.
+                    * If it's "const = const" then just ignore it altogether.
+                    * There is no place in the restrictinfo structure to
+                    * store it.  (If the two consts are in fact unequal, then
+                    * propagating the comparison to Vars will cause us to
+                    * produce zero rows out, as expected.)
                     */
-                   bool        delete_it = (have_consts &&
-                                            i1_is_variable &&
-                                            i2_is_variable);
-
-                   process_implied_equality(root,
-                                            item1->key, item2->key,
-                                            item1->sortop, item2->sortop,
-                                            relids[i1], relids[i2],
-                                            delete_it);
+                   if (i1_is_variable || i2_is_variable)
+                   {
+                       /*
+                        * Tell process_implied_equality to delete the clause,
+                        * not add it, if it's "var = var" and we have
+                        * constants present in the list.
+                        */
+                       bool        delete_it = (have_consts &&
+                                                i1_is_variable &&
+                                                i2_is_variable);
+
+                       process_implied_equality(root,
+                                                item1->key, item2->key,
+                                                item1->sortop, item2->sortop,
+                                                relids[i1], relids[i2],
+                                                delete_it);
+                   }
+                   i2++;
                }
-               i2++;
+               i1++;
            }
-           i1++;
        }
+
+       /*
+        * If we have constant(s) and outer joins, try to propagate the
+        * constants through outer-join quals.
+        */
+       if (have_consts && root->hasOuterJoins)
+           generate_outer_join_implications(root, curset, relids);
    }
 }
 
@@ -362,118 +366,154 @@ generate_outer_join_implications(PlannerInfo *root,
                                 List *equi_key_set,
                                 Relids *relids)
 {
-   ListCell   *l1;
+   ListCell   *l;
+   int         i = 0;
 
-   /* Examine each mergejoinable outer-join clause with OUTERVAR on left */
-   foreach(l1, root->left_join_clauses)
+   /* Process each non-constant element of equi_key_set */
+   foreach(l, equi_key_set)
    {
-       RestrictInfo *rinfo = (RestrictInfo *) lfirst(l1);
-       Node   *leftop = get_leftop(rinfo->clause);
-       Node   *rightop = get_rightop(rinfo->clause);
-       ListCell   *l2;
+       PathKeyItem *item1 = (PathKeyItem *) lfirst(l);
 
-       /* Scan to see if it matches any element of equi_key_set */
-       foreach(l2, equi_key_set)
+       if (!bms_is_empty(relids[i]))
        {
-           PathKeyItem *item1 = (PathKeyItem *) lfirst(l2);
+           sub_generate_join_implications(root, equi_key_set, relids,
+                                          item1->key,
+                                          item1->sortop,
+                                          relids[i]);
+       }
+       i++;
+   }
+}
 
-           if (equal(leftop, item1->key) &&
-               rinfo->left_sortop == item1->sortop)
-           {
-               /*
-                * Yes, so find constant member(s) of set and generate
-                * implied INNERVAR = CONSTANT
-                */
-               process_implied_const_eq(root, equi_key_set, relids,
-                                        rightop,
-                                        rinfo->right_sortop,
-                                        rinfo->right_relids,
-                                        false);
-               /*
-                * We can remove the explicit outer join qual, too,
-                * since we now have tests forcing each of its sides
-                * to the same value.
-                */
-               process_implied_equality(root,
-                                        leftop,
-                                        rightop,
-                                        rinfo->left_sortop,
-                                        rinfo->right_sortop,
-                                        rinfo->left_relids,
-                                        rinfo->right_relids,
-                                        true);
+/*
+ * sub_generate_join_implications
+ *   Propagate a constant equality through outer join clauses.
+ *
+ * The item described by item1/sortop1/item1_relids has been determined
+ * to be equal to the constant(s) listed in equi_key_set.  Recursively
+ * trace out the implications of this.
+ *
+ * equi_key_set and relids are as for generate_outer_join_implications.
+ */
+static void
+sub_generate_join_implications(PlannerInfo *root,
+                               List *equi_key_set, Relids *relids,
+                               Node *item1, Oid sortop1, Relids item1_relids)
 
-               /* No need to match against remaining set members */
-               break;
-           }
+{
+   ListCell   *l;
+
+   /*
+    * Examine each mergejoinable outer-join clause with OUTERVAR on left,
+    * looking for an OUTERVAR identical to item1
+    */
+   foreach(l, root->left_join_clauses)
+   {
+       RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
+       Node   *leftop = get_leftop(rinfo->clause);
+
+       if (equal(leftop, item1) && rinfo->left_sortop == sortop1)
+       {
+           /*
+            * Match, so find constant member(s) of set and generate
+            * implied INNERVAR = CONSTANT
+            */
+           Node   *rightop = get_rightop(rinfo->clause);
+
+           process_implied_const_eq(root, equi_key_set, relids,
+                                    rightop,
+                                    rinfo->right_sortop,
+                                    rinfo->right_relids,
+                                    false);
+           /*
+            * We can remove explicit tests of this outer-join qual, too,
+            * since we now have tests forcing each of its sides
+            * to the same value.
+            */
+           process_implied_equality(root,
+                                    leftop, rightop,
+                                    rinfo->left_sortop, rinfo->right_sortop,
+                                    rinfo->left_relids, rinfo->right_relids,
+                                    true);
+           /*
+            * And recurse to see if we can deduce anything from
+            * INNERVAR = CONSTANT
+            */
+           sub_generate_join_implications(root, equi_key_set, relids,
+                                          rightop,
+                                          rinfo->right_sortop,
+                                          rinfo->right_relids);
        }
    }
 
-   /* Examine each mergejoinable outer-join clause with OUTERVAR on right */
-   foreach(l1, root->right_join_clauses)
+   /* The same, looking at clauses with OUTERVAR on right */
+   foreach(l, root->right_join_clauses)
    {
-       RestrictInfo *rinfo = (RestrictInfo *) lfirst(l1);
-       Node   *leftop = get_leftop(rinfo->clause);
+       RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
        Node   *rightop = get_rightop(rinfo->clause);
-       ListCell   *l2;
 
-       /* Scan to see if it matches any element of equi_key_set */
-       foreach(l2, equi_key_set)
+       if (equal(rightop, item1) && rinfo->right_sortop == sortop1)
        {
-           PathKeyItem *item1 = (PathKeyItem *) lfirst(l2);
-
-           if (equal(rightop, item1->key) &&
-               rinfo->right_sortop == item1->sortop)
-           {
-               /*
-                * Yes, so find constant member(s) of set and generate
-                * implied INNERVAR = CONSTANT
-                */
-               process_implied_const_eq(root, equi_key_set, relids,
-                                        leftop,
-                                        rinfo->left_sortop,
-                                        rinfo->left_relids,
-                                        false);
-               /*
-                * We can remove the explicit outer join qual, too,
-                * since we now have tests forcing each of its sides
-                * to the same value.
-                */
-               process_implied_equality(root,
-                                        leftop,
-                                        rightop,
-                                        rinfo->left_sortop,
-                                        rinfo->right_sortop,
-                                        rinfo->left_relids,
-                                        rinfo->right_relids,
-                                        true);
+           /*
+            * Match, so find constant member(s) of set and generate
+            * implied INNERVAR = CONSTANT
+            */
+           Node   *leftop = get_leftop(rinfo->clause);
 
-               /* No need to match against remaining set members */
-               break;
-           }
+           process_implied_const_eq(root, equi_key_set, relids,
+                                    leftop,
+                                    rinfo->left_sortop,
+                                    rinfo->left_relids,
+                                    false);
+           /*
+            * We can remove explicit tests of this outer-join qual, too,
+            * since we now have tests forcing each of its sides
+            * to the same value.
+            */
+           process_implied_equality(root,
+                                    leftop, rightop,
+                                    rinfo->left_sortop, rinfo->right_sortop,
+                                    rinfo->left_relids, rinfo->right_relids,
+                                    true);
+           /*
+            * And recurse to see if we can deduce anything from
+            * INNERVAR = CONSTANT
+            */
+           sub_generate_join_implications(root, equi_key_set, relids,
+                                          leftop,
+                                          rinfo->left_sortop,
+                                          rinfo->left_relids);
        }
    }
 
-   /* Examine each mergejoinable full-join clause */
-   foreach(l1, root->full_join_clauses)
+   /*
+    * Only COALESCE(x,y) items can possibly match full joins
+    */
+   if (IsA(item1, CoalesceExpr))
    {
-       RestrictInfo *rinfo = (RestrictInfo *) lfirst(l1);
-       Node   *leftop = get_leftop(rinfo->clause);
-       Node   *rightop = get_rightop(rinfo->clause);
-       int i1 = 0;
-       ListCell   *l2;
+       CoalesceExpr *cexpr = (CoalesceExpr *) item1;
+       Node   *cfirst;
+       Node   *csecond;
 
-       /* Scan to see if it matches any element of equi_key_set */
-       foreach(l2, equi_key_set)
+       if (list_length(cexpr->args) != 2)
+           return;
+       cfirst = (Node *) linitial(cexpr->args);
+       csecond = (Node *) lsecond(cexpr->args);
+
+       /*
+        * Examine each mergejoinable full-join clause, looking for a
+        * clause of the form "x = y" matching the COALESCE(x,y) expression
+        */
+       foreach(l, root->full_join_clauses)
        {
-           PathKeyItem *item1 = (PathKeyItem *) lfirst(l2);
-           CoalesceExpr *cexpr = (CoalesceExpr *) item1->key;
+           RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
+           Node   *leftop = get_leftop(rinfo->clause);
+           Node   *rightop = get_rightop(rinfo->clause);
 
            /*
-            * Try to match a pathkey containing a COALESCE() expression
-            * to the join clause.  We can assume the COALESCE() inputs
-            * are in the same order as the join clause, since both were
-            * automatically generated in the cases we care about.
+            * We can assume the COALESCE() inputs are in the same order
+            * as the join clause, since both were automatically generated
+            * in the cases we care about.
             *
             * XXX currently this may fail to match in cross-type cases
             * because the COALESCE will contain typecast operations while
@@ -482,15 +522,13 @@ generate_outer_join_implications(PlannerInfo *root,
             * Is it OK to strip implicit coercions from the COALESCE
             * arguments?  What of the sortops in such cases?
             */
-           if (IsA(cexpr, CoalesceExpr) &&
-               list_length(cexpr->args) == 2 &&
-               equal(leftop, (Node *) linitial(cexpr->args)) &&
-               equal(rightop, (Node *) lsecond(cexpr->args)) &&
-               rinfo->left_sortop == item1->sortop &&
-               rinfo->right_sortop == item1->sortop)
+           if (equal(leftop, cfirst) &&
+               equal(rightop, csecond) &&
+               rinfo->left_sortop == sortop1 &&
+               rinfo->right_sortop == sortop1)
            {
                /*
-                * Yes, so find constant member(s) of set and generate
+                * Match, so find constant member(s) of set and generate
                 * implied LEFTVAR = CONSTANT
                 */
                process_implied_const_eq(root, equi_key_set, relids,
@@ -506,28 +544,37 @@ generate_outer_join_implications(PlannerInfo *root,
                                         false);
                /* ... and remove COALESCE() = CONSTANT */
                process_implied_const_eq(root, equi_key_set, relids,
-                                        item1->key,
-                                        item1->sortop,
-                                        relids[i1],
+                                        item1,
+                                        sortop1,
+                                        item1_relids,
                                         true);
                /*
-                * We can remove the explicit outer join qual, too,
+                * We can remove explicit tests of this outer-join qual, too,
                 * since we now have tests forcing each of its sides
                 * to the same value.
                 */
                process_implied_equality(root,
-                                        leftop,
-                                        rightop,
+                                        leftop, rightop,
                                         rinfo->left_sortop,
                                         rinfo->right_sortop,
                                         rinfo->left_relids,
                                         rinfo->right_relids,
                                         true);
+               /*
+                * And recurse to see if we can deduce anything from
+                * LEFTVAR = CONSTANT
+                */
+               sub_generate_join_implications(root, equi_key_set, relids,
+                                              leftop,
+                                              rinfo->left_sortop,
+                                              rinfo->left_relids);
+               /* ... and RIGHTVAR = CONSTANT */
+               sub_generate_join_implications(root, equi_key_set, relids,
+                                              rightop,
+                                              rinfo->right_sortop,
+                                              rinfo->right_relids);
 
-               /* No need to match against remaining set members */
-               break;
            }
-           i1++;
        }
    }
 }
@@ -537,10 +584,8 @@ generate_outer_join_implications(PlannerInfo *root,
  *   Apply process_implied_equality with the given item and each
  *   pseudoconstant member of equi_key_set.
  *
- * This is just a subroutine to save some cruft in
- * generate_outer_join_implications.  equi_key_set and relids are as in
- * generate_outer_join_implications, the other parameters as for
- * process_implied_equality.
+ * equi_key_set and relids are as for generate_outer_join_implications,
+ * the other parameters as for process_implied_equality.
  */
 static void
 process_implied_const_eq(PlannerInfo *root, List *equi_key_set, Relids *relids,