]> granicus.if.org Git - postgresql/commitdiff
Improve outer-join-deduction logic to be able to propagate equalities
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 3 Jul 2005 18:26:32 +0000 (18:26 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
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,