]> granicus.if.org Git - postgresql/commitdiff
Teach planner about some cases where a restriction clause can be
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 2 Jul 2005 23:00:42 +0000 (23:00 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 2 Jul 2005 23:00:42 +0000 (23:00 +0000)
propagated inside an outer join.  In particular, given
LEFT JOIN ON (A = B) WHERE A = constant, we cannot conclude that
B = constant at the top level (B might be null instead), but we
can nonetheless put a restriction B = constant into the quals for
B's relation, since no inner-side rows not meeting that condition
can contribute to the final result.  Similarly, given
FULL JOIN USING (J) WHERE J = constant, we can't directly conclude
that either input J variable = constant, but it's OK to push such
quals into each input rel.  Per recent gripe from Kim Bisgaard.
Along the way, remove 'valid_everywhere' flag from RestrictInfo,
as on closer analysis it was not being used for anything, and was
defined backwards anyway.

13 files changed:
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/optimizer/path/indxpath.c
src/backend/optimizer/path/orindxpath.c
src/backend/optimizer/path/pathkeys.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/initsplan.c
src/backend/optimizer/plan/planmain.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/util/restrictinfo.c
src/include/nodes/relation.h
src/include/optimizer/restrictinfo.h

index ce16d2eba182fec697bfa0b626114839132495cf..8d42cead085d365071faa1035de3f99de53d3b2c 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.310 2005/06/28 05:08:56 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.311 2005/07/02 23:00:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1249,7 +1249,6 @@ _copyRestrictInfo(RestrictInfo *from)
 
        COPY_NODE_FIELD(clause);
        COPY_SCALAR_FIELD(is_pushed_down);
-       COPY_SCALAR_FIELD(valid_everywhere);
        COPY_SCALAR_FIELD(can_join);
        COPY_BITMAPSET_FIELD(clause_relids);
        COPY_BITMAPSET_FIELD(required_relids);
index ade4f16a090885c880ed1e4ab42479aad84a5701..e8c8a0cbc94c467bb75ab017a2f3b436fe780b7b 100644 (file)
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.247 2005/06/28 05:08:57 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.248 2005/07/02 23:00:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -603,7 +603,6 @@ _equalRestrictInfo(RestrictInfo *a, RestrictInfo *b)
 {
        COMPARE_NODE_FIELD(clause);
        COMPARE_SCALAR_FIELD(is_pushed_down);
-       COMPARE_SCALAR_FIELD(valid_everywhere);
        COMPARE_BITMAPSET_FIELD(required_relids);
 
        /*
index 81fb9c88fa6d2bff3402fff4bc4917e90c7373ce..0b905dd043d3e95a37c34856b65157581cb45262 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.257 2005/06/28 05:08:57 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.258 2005/07/02 23:00:39 tgl Exp $
  *
  * NOTES
  *       Every node type that can appear in stored rules' parsetrees *must*
@@ -1164,9 +1164,13 @@ _outPlannerInfo(StringInfo str, PlannerInfo *node)
        WRITE_NODE_FIELD(parse);
        WRITE_NODE_FIELD(join_rel_list);
        WRITE_NODE_FIELD(equi_key_list);
+       WRITE_NODE_FIELD(left_join_clauses);
+       WRITE_NODE_FIELD(right_join_clauses);
+       WRITE_NODE_FIELD(full_join_clauses);
        WRITE_NODE_FIELD(in_info_list);
        WRITE_NODE_FIELD(query_pathkeys);
        WRITE_BOOL_FIELD(hasJoinRTEs);
+       WRITE_BOOL_FIELD(hasOuterJoins);
        WRITE_BOOL_FIELD(hasHavingQual);
 }
 
@@ -1234,7 +1238,6 @@ _outRestrictInfo(StringInfo str, RestrictInfo *node)
        /* NB: this isn't a complete set of fields */
        WRITE_NODE_FIELD(clause);
        WRITE_BOOL_FIELD(is_pushed_down);
-       WRITE_BOOL_FIELD(valid_everywhere);
        WRITE_BOOL_FIELD(can_join);
        WRITE_BITMAPSET_FIELD(clause_relids);
        WRITE_BITMAPSET_FIELD(required_relids);
index be684094119c5b7f7588502edbb1ab86c187e07a..07b67a88228174e30d632ed841a48bb028a5461d 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.185 2005/06/14 04:04:30 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.186 2005/07/02 23:00:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1882,7 +1882,7 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
                                {
                                        resultquals = lappend(resultquals,
                                                                                  make_restrictinfo(boolqual,
-                                                                                                                       true, true,
+                                                                                                                       true,
                                                                                                                        NULL));
                                        continue;
                                }
@@ -2132,7 +2132,7 @@ prefix_quals(Node *leftop, Oid opclass,
                        elog(ERROR, "no = operator for opclass %u", opclass);
                expr = make_opclause(oproid, BOOLOID, false,
                                                         (Expr *) leftop, (Expr *) prefix_const);
-               result = list_make1(make_restrictinfo(expr, true, true, NULL));
+               result = list_make1(make_restrictinfo(expr, true, NULL));
                return result;
        }
 
@@ -2147,7 +2147,7 @@ prefix_quals(Node *leftop, Oid opclass,
                elog(ERROR, "no >= operator for opclass %u", opclass);
        expr = make_opclause(oproid, BOOLOID, false,
                                                 (Expr *) leftop, (Expr *) prefix_const);
-       result = list_make1(make_restrictinfo(expr, true, true, NULL));
+       result = list_make1(make_restrictinfo(expr, true, NULL));
 
        /*-------
         * If we can create a string larger than the prefix, we can say
@@ -2163,7 +2163,7 @@ prefix_quals(Node *leftop, Oid opclass,
                        elog(ERROR, "no < operator for opclass %u", opclass);
                expr = make_opclause(oproid, BOOLOID, false,
                                                         (Expr *) leftop, (Expr *) greaterstr);
-               result = lappend(result, make_restrictinfo(expr, true, true, NULL));
+               result = lappend(result, make_restrictinfo(expr, true, NULL));
        }
 
        return result;
@@ -2234,7 +2234,7 @@ network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass, Datum rightop)
                                                 (Expr *) leftop,
                                                 (Expr *) makeConst(datatype, -1, opr1right,
                                                                                        false, false));
-       result = list_make1(make_restrictinfo(expr, true, true, NULL));
+       result = list_make1(make_restrictinfo(expr, true, NULL));
 
        /* create clause "key <= network_scan_last( rightop )" */
 
@@ -2249,7 +2249,7 @@ network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass, Datum rightop)
                                                 (Expr *) leftop,
                                                 (Expr *) makeConst(datatype, -1, opr2right,
                                                                                        false, false));
-       result = lappend(result, make_restrictinfo(expr, true, true, NULL));
+       result = lappend(result, make_restrictinfo(expr, true, NULL));
 
        return result;
 }
index 7eadd220b949ba085ac71c7e762698a996631883..884329edd9723f5b130576fb1db3119f5279cddb 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/path/orindxpath.c,v 1.72 2005/06/09 04:18:59 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/path/orindxpath.c,v 1.73 2005/07/02 23:00:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -90,16 +90,13 @@ create_or_index_quals(PlannerInfo *root, RelOptInfo *rel)
        ListCell   *i;
 
        /*
-        * Find potentially interesting OR joinclauses.  We must ignore any
-        * joinclauses that are not marked valid_everywhere, because they
-        * cannot be pushed down due to outer-join rules.
+        * Find potentially interesting OR joinclauses.
         */
        foreach(i, rel->joininfo)
        {
                RestrictInfo *rinfo = (RestrictInfo *) lfirst(i);
 
-               if (restriction_is_or_clause(rinfo) &&
-                       rinfo->valid_everywhere)
+               if (restriction_is_or_clause(rinfo))
                {
                        /*
                         * Use the generate_bitmap_or_paths() machinery to estimate
@@ -140,8 +137,7 @@ create_or_index_quals(PlannerInfo *root, RelOptInfo *rel)
         * Convert the path's indexclauses structure to a RestrictInfo tree,
         * and add it to the rel's restriction list.
         */
-       newrinfos = make_restrictinfo_from_bitmapqual((Path *) bestpath,
-                                                                                                 true, true);
+       newrinfos = make_restrictinfo_from_bitmapqual((Path *) bestpath, true);
        Assert(list_length(newrinfos) == 1);
        or_rinfo = (RestrictInfo *) linitial(newrinfos);
        Assert(IsA(or_rinfo, RestrictInfo));
index 52075fbf4656c2e9a8d8d3b676ccd76a2890f1f5..389d89f0d8da80682dc3b6f35042948513ee38c9 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.68 2005/06/09 04:18:59 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/path/pathkeys.c,v 1.69 2005/07/02 23:00:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 
 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 process_implied_const_eq(PlannerInfo *root,
+                                                                        List *equi_key_set, Relids *relids,
+                                                                        Node *item1, Oid sortop1,
+                                                                        Relids item1_relids,
+                                                                        bool delete_it);
 static List *make_canonical_pathkey(PlannerInfo *root, PathKeyItem *item);
 static Var *find_indexkey_var(PlannerInfo *root, RelOptInfo *rel,
                                  AttrNumber varattno);
@@ -193,6 +201,10 @@ add_equijoined_keys(PlannerInfo *root, RestrictInfo *restrictinfo)
  * functions; but we will never consider such an expression to be a pathkey
  * at all, because check_mergejoinable() will reject it.)
  *
+ * Also, when we have constants in an equi_key_list we can try to propagate
+ * the constants into outer joins; see generate_outer_join_implications
+ * for discussion.
+ *
  * This routine just walks the equi_key_list to find all pairwise equalities.
  * We call process_implied_equality (in plan/initsplan.c) to adjust the
  * restrictinfo datastructures for each pair.
@@ -213,9 +225,10 @@ generate_implied_equalities(PlannerInfo *root)
 
                /*
                 * A set containing only two items cannot imply any equalities
-                * beyond the one that created the set, so we can skip it.
+                * beyond the one that created the set, so we can skip it ---
+                * unless outer joins appear in the query.
                 */
-               if (nitems < 3)
+               if (nitems < 3 && !root->hasOuterJoins)
                        continue;
 
                /*
@@ -237,6 +250,20 @@ 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).
@@ -285,6 +312,264 @@ generate_implied_equalities(PlannerInfo *root)
        }
 }
 
+/*
+ * generate_outer_join_implications
+ *       Generate clauses that can be deduced in outer-join situations.
+ *
+ * When we have mergejoinable clauses A = B that are outer-join clauses,
+ * we can't blindly combine them with other clauses A = C to deduce B = C,
+ * since in fact the "equality" A = B won't necessarily hold above the
+ * outer join (one of the variables might be NULL instead).  Nonetheless
+ * there are cases where we can add qual clauses using transitivity.
+ *
+ * One case that we look for here is an outer-join clause OUTERVAR = INNERVAR
+ * combined with a pushed-down (valid everywhere) clause OUTERVAR = CONSTANT.
+ * It is safe and useful to push a clause INNERVAR = CONSTANT into the
+ * evaluation of the inner (nullable) relation, because any inner rows not
+ * meeting this condition will not contribute to the outer-join result anyway.
+ * (Any outer rows they could join to will be eliminated by the pushed-down
+ * clause.)
+ *
+ * Note that the above rule does not work for full outer joins, nor for
+ * pushed-down restrictions on an inner-side variable; nor is it very
+ * interesting to consider cases where the pushed-down clause involves
+ * relations entirely outside the outer join, since such clauses couldn't
+ * be pushed into the inner side's scan anyway.  So the restriction to
+ * outervar = pseudoconstant is not really giving up anything.
+ *
+ * For full-join cases, we can only do something useful if it's a FULL JOIN
+ * USING and a merged column has a restriction MERGEDVAR = CONSTANT.  By
+ * the time it gets here, the restriction will look like
+ *             COALESCE(LEFTVAR, RIGHTVAR) = CONSTANT
+ * and we will have a join clause LEFTVAR = RIGHTVAR that we can match the
+ * COALESCE expression to.  In this situation we can push LEFTVAR = CONSTANT
+ * and RIGHTVAR = CONSTANT into the input relations, since any rows not
+ * meeting these conditions cannot contribute to the join result.
+ *
+ * Again, there isn't any traction to be gained by trying to deal with
+ * clauses comparing a mergedvar to a non-pseudoconstant.  So we can make
+ * use of the equi_key_lists to quickly find the interesting pushed-down
+ * clauses.  The interesting outer-join clauses were accumulated for us by
+ * distribute_qual_to_rels.
+ *
+ * equi_key_set: a list of PathKeyItems that are known globally equivalent,
+ * at least one of which is a pseudoconstant.
+ * relids: an array of Relids sets showing the relation membership of each
+ * PathKeyItem in equi_key_set.
+ */
+static void
+generate_outer_join_implications(PlannerInfo *root,
+                                                                List *equi_key_set,
+                                                                Relids *relids)
+{
+       ListCell   *l1;
+
+       /* Examine each mergejoinable outer-join clause with OUTERVAR on left */
+       foreach(l1, root->left_join_clauses)
+       {
+               RestrictInfo *rinfo = (RestrictInfo *) lfirst(l1);
+               Node   *leftop = get_leftop(rinfo->clause);
+               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)
+               {
+                       PathKeyItem *item1 = (PathKeyItem *) lfirst(l2);
+
+                       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);
+
+                               /* No need to match against remaining set members */
+                               break;
+                       }
+               }
+       }
+
+       /* Examine each mergejoinable outer-join clause with OUTERVAR on right */
+       foreach(l1, root->right_join_clauses)
+       {
+               RestrictInfo *rinfo = (RestrictInfo *) lfirst(l1);
+               Node   *leftop = get_leftop(rinfo->clause);
+               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)
+               {
+                       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);
+
+                               /* No need to match against remaining set members */
+                               break;
+                       }
+               }
+       }
+
+       /* Examine each mergejoinable full-join clause */
+       foreach(l1, root->full_join_clauses)
+       {
+               RestrictInfo *rinfo = (RestrictInfo *) lfirst(l1);
+               Node   *leftop = get_leftop(rinfo->clause);
+               Node   *rightop = get_rightop(rinfo->clause);
+               int i1 = 0;
+               ListCell   *l2;
+
+               /* Scan to see if it matches any element of equi_key_set */
+               foreach(l2, equi_key_set)
+               {
+                       PathKeyItem *item1 = (PathKeyItem *) lfirst(l2);
+                       CoalesceExpr *cexpr = (CoalesceExpr *) item1->key;
+
+                       /*
+                        * 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.
+                        *
+                        * XXX currently this may fail to match in cross-type cases
+                        * because the COALESCE will contain typecast operations while
+                        * the join clause may not (if there is a cross-type mergejoin
+                        * operator available for the two column types).
+                        * 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)
+                       {
+                               /*
+                                * Yes, so find constant member(s) of set and generate
+                                * implied LEFTVAR = CONSTANT
+                                */
+                               process_implied_const_eq(root, equi_key_set, relids,
+                                                                                leftop,
+                                                                                rinfo->left_sortop,
+                                                                                rinfo->left_relids,
+                                                                                false);
+                               /* ... and RIGHTVAR = CONSTANT */
+                               process_implied_const_eq(root, equi_key_set, relids,
+                                                                                rightop,
+                                                                                rinfo->right_sortop,
+                                                                                rinfo->right_relids,
+                                                                                false);
+                               /* ... and remove COALESCE() = CONSTANT */
+                               process_implied_const_eq(root, equi_key_set, relids,
+                                                                                item1->key,
+                                                                                item1->sortop,
+                                                                                relids[i1],
+                                                                                true);
+                               /*
+                                * 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);
+
+                               /* No need to match against remaining set members */
+                               break;
+                       }
+                       i1++;
+               }
+       }
+}
+
+/*
+ * process_implied_const_eq
+ *       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.
+ */
+static void
+process_implied_const_eq(PlannerInfo *root, List *equi_key_set, Relids *relids,
+                                                Node *item1, Oid sortop1, Relids item1_relids,
+                                                bool delete_it)
+{
+       ListCell   *l;
+       bool            found = false;
+       int                     i = 0;
+
+       foreach(l, equi_key_set)
+       {
+               PathKeyItem *item2 = (PathKeyItem *) lfirst(l);
+
+               if (bms_is_empty(relids[i]))
+               {
+                       process_implied_equality(root,
+                                                                        item1, item2->key,
+                                                                        sortop1, item2->sortop,
+                                                                        item1_relids, NULL,
+                                                                        delete_it);
+                       found = true;
+               }
+               i++;
+       }
+       /* Caller screwed up if no constants in list */
+       Assert(found);
+}
+
 /*
  * exprs_known_equal
  *       Detect whether two expressions are known equal due to equijoin clauses.
index 76812e2e4b32b229321919165ebc0a7a0a317dc5..959c17206c082485d690fcf036c49b859d416ca0 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.192 2005/06/10 22:25:36 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.193 2005/07/02 23:00:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1176,8 +1176,7 @@ create_nestloop_plan(PlannerInfo *root,
                        List       *bitmapclauses;
 
                        bitmapclauses =
-                               make_restrictinfo_from_bitmapqual(innerpath->bitmapqual,
-                                                                                                 true, true);
+                               make_restrictinfo_from_bitmapqual(innerpath->bitmapqual, true);
                        joinrestrictclauses =
                                select_nonredundant_join_clauses(root,
                                                                                                 joinrestrictclauses,
index f7066e4906ce9e8f73d8109cfb969689c8d43e20..91a23d6d1c3e9ac95aabd1f8d4156b6111063a82 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.107 2005/06/09 04:18:59 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.108 2005/07/02 23:00:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -374,8 +374,8 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
                                                Relids qualscope)
 {
        Relids          relids;
-       bool            valid_everywhere;
-       bool            can_be_equijoin;
+       bool            maybe_equijoin;
+       bool            maybe_outer_join;
        RestrictInfo *restrictinfo;
        RelOptInfo *rel;
        List       *vars;
@@ -409,14 +409,15 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
        if (isdeduced)
        {
                /*
-                * If the qual came from implied-equality deduction, we can
-                * evaluate the qual at its natural semantic level.  It is not
-                * affected by any outer-join rules (else we'd not have decided
-                * the vars were equal).
+                * If the qual came from implied-equality deduction, we always
+                * evaluate the qual at its natural semantic level.  It is the
+                * responsibility of the deducer not to create any quals that
+                * should be delayed by outer-join rules.
                 */
                Assert(bms_equal(relids, qualscope));
-               valid_everywhere = true;
-               can_be_equijoin = true;
+               /* Needn't feed it back for more deductions */
+               maybe_equijoin = false;
+               maybe_outer_join = false;
        }
        else if (bms_overlap(relids, outerjoin_nonnullable))
        {
@@ -434,8 +435,14 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
                 * result, so we treat it the same as an ordinary inner-join qual.
                 */
                relids = qualscope;
-               valid_everywhere = false;
-               can_be_equijoin = false;
+               /*
+                * We can't use such a clause to deduce equijoin (the left and
+                * right sides might be unequal above the join because one of
+                * them has gone to NULL) ... but we might be able to use it
+                * for more limited purposes.
+                */
+               maybe_equijoin = false;
+               maybe_outer_join = true;
        }
        else
        {
@@ -449,34 +456,25 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
                 * time we are called, the outerjoinset of each baserel will show
                 * exactly those outer joins that are below the qual in the join
                 * tree.
-                *
-                * We also need to determine whether the qual is "valid everywhere",
-                * which is true if the qual mentions no variables that are
-                * involved in lower-level outer joins (this may be an overly
-                * strong test).
                 */
                Relids          addrelids = NULL;
                Relids          tmprelids;
                int                     relno;
 
-               valid_everywhere = true;
                tmprelids = bms_copy(relids);
                while ((relno = bms_first_member(tmprelids)) >= 0)
                {
                        RelOptInfo *rel = find_base_rel(root, relno);
 
                        if (rel->outerjoinset != NULL)
-                       {
                                addrelids = bms_add_members(addrelids, rel->outerjoinset);
-                               valid_everywhere = false;
-                       }
                }
                bms_free(tmprelids);
 
                if (bms_is_subset(addrelids, relids))
                {
                        /* Qual is not affected by any outer-join restriction */
-                       can_be_equijoin = true;
+                       maybe_equijoin = true;
                }
                else
                {
@@ -488,9 +486,10 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
                         * Because application of the qual will be delayed by outer
                         * join, we mustn't assume its vars are equal everywhere.
                         */
-                       can_be_equijoin = false;
+                       maybe_equijoin = false;
                }
                bms_free(addrelids);
+               maybe_outer_join = false;
        }
 
        /*
@@ -508,7 +507,6 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
         */
        restrictinfo = make_restrictinfo((Expr *) clause,
                                                                         is_pushed_down,
-                                                                        valid_everywhere,
                                                                         relids);
 
        /*
@@ -533,8 +531,7 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
                         * allows us to consider z and q equal after their rels are
                         * joined.
                         */
-                       if (can_be_equijoin)
-                               check_mergejoinable(restrictinfo);
+                       check_mergejoinable(restrictinfo);
 
                        /*
                         * If the clause was deduced from implied equality, check to
@@ -601,18 +598,60 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
        }
 
        /*
-        * If the clause has a mergejoinable operator, and is not an
-        * outer-join qualification nor bubbled up due to an outer join, then
-        * the two sides represent equivalent PathKeyItems for path keys: any
-        * path that is sorted by one side will also be sorted by the other
-        * (as soon as the two rels are joined, that is).  Record the key
-        * equivalence for future use.  (We can skip this for a deduced
-        * clause, since the keys are already known equivalent in that case.)
+        * If the clause has a mergejoinable operator, we may be able to
+        * deduce more things from it under the principle of transitivity.
+        *
+        * If it is not an outer-join qualification nor bubbled up due to an outer
+        * join, then the two sides represent equivalent PathKeyItems for path
+        * keys: any path that is sorted by one side will also be sorted by the
+        * other (as soon as the two rels are joined, that is).  Pass such clauses
+        * to add_equijoined_keys.
+        *
+        * If it is a left or right outer-join qualification that relates the two
+        * sides of the outer join (no funny business like leftvar1 = leftvar2 +
+        * rightvar), we add it to root->left_join_clauses or
+        * root->right_join_clauses according to which side the nonnullable
+        * variable appears on.
+        *
+        * If it is a full outer-join qualification, we add it to
+        * root->full_join_clauses.  (Ideally we'd discard cases that aren't
+        * leftvar = rightvar, as we do for left/right joins, but this routine
+        * doesn't have the info needed to do that; and the current usage of the
+        * full_join_clauses list doesn't require that, so it's not currently
+        * worth complicating this routine's API to make it possible.)
         */
-       if (can_be_equijoin &&
-               restrictinfo->mergejoinoperator != InvalidOid &&
-               !isdeduced)
-               add_equijoined_keys(root, restrictinfo);
+       if (restrictinfo->mergejoinoperator != InvalidOid)
+       {
+               if (maybe_equijoin)
+                       add_equijoined_keys(root, restrictinfo);
+               else if (maybe_outer_join && restrictinfo->can_join)
+               {
+                       if (bms_is_subset(restrictinfo->left_relids,
+                                                         outerjoin_nonnullable) &&
+                               !bms_overlap(restrictinfo->right_relids,
+                                                        outerjoin_nonnullable))
+                       {
+                               /* we have outervar = innervar */
+                               root->left_join_clauses = lappend(root->left_join_clauses,
+                                                                                                 restrictinfo);
+                       }
+                       else if (bms_is_subset(restrictinfo->right_relids,
+                                                                  outerjoin_nonnullable) &&
+                                        !bms_overlap(restrictinfo->left_relids,
+                                                                 outerjoin_nonnullable))
+                       {
+                               /* we have innervar = outervar */
+                               root->right_join_clauses = lappend(root->right_join_clauses,
+                                                                                                  restrictinfo);
+                       }
+                       else if (bms_equal(outerjoin_nonnullable, qualscope))
+                       {
+                               /* FULL JOIN (above tests cannot match in this case) */
+                               root->full_join_clauses = lappend(root->full_join_clauses,
+                                                                                                 restrictinfo);
+                       }
+               }
+       }
 }
 
 /*
index 788fd2f3249e005d8fbd5188a7da18fd3f9ee75c..7038a45ac64aac657f336cd694924ca7224cb77d 100644 (file)
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.85 2005/06/10 03:32:23 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.86 2005/07/02 23:00:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -121,6 +121,9 @@ query_planner(PlannerInfo *root, List *tlist, double tuple_fraction,
        root->join_rel_list = NIL;
        root->join_rel_hash = NULL;
        root->equi_key_list = NIL;
+       root->left_join_clauses = NIL;
+       root->right_join_clauses = NIL;
+       root->full_join_clauses = NIL;
 
        /*
         * Construct RelOptInfo nodes for all base relations in query.
index df8d0556b42cfdc9dbbce31abe716811d8664769..334f8504dff2b0c4e4cb04e2c913b80b04190386 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.189 2005/06/10 02:21:04 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.190 2005/07/02 23:00:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -194,7 +194,6 @@ subquery_planner(Query *parse, double tuple_fraction,
        int                     saved_planid = PlannerPlanId;
        PlannerInfo *root;
        Plan       *plan;
-       bool            hasOuterJoins;
        List       *newHaving;
        List       *lst;
        ListCell   *l;
@@ -228,12 +227,16 @@ subquery_planner(Query *parse, double tuple_fraction,
        /*
         * Detect whether any rangetable entries are RTE_JOIN kind; if not, we
         * can avoid the expense of doing flatten_join_alias_vars().  Also
-        * check for outer joins --- if none, we can skip
-        * reduce_outer_joins(). This must be done after we have done
+        * check for outer joins --- if none, we can skip reduce_outer_joins()
+        * and some other processing.  This must be done after we have done
         * pull_up_subqueries, of course.
+        *
+        * Note: if reduce_outer_joins manages to eliminate all outer joins,
+        * root->hasOuterJoins is not reset currently.  This is OK since its
+        * purpose is merely to suppress unnecessary processing in simple cases.
         */
        root->hasJoinRTEs = false;
-       hasOuterJoins = false;
+       root->hasOuterJoins = false;
        foreach(l, parse->rtable)
        {
                RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
@@ -243,7 +246,7 @@ subquery_planner(Query *parse, double tuple_fraction,
                        root->hasJoinRTEs = true;
                        if (IS_OUTER_JOIN(rte->jointype))
                        {
-                               hasOuterJoins = true;
+                               root->hasOuterJoins = true;
                                /* Can quit scanning once we find an outer join */
                                break;
                        }
@@ -347,7 +350,7 @@ subquery_planner(Query *parse, double tuple_fraction,
         * joins. This step is most easily done after we've done expression
         * preprocessing.
         */
-       if (hasOuterJoins)
+       if (root->hasOuterJoins)
                reduce_outer_joins(root);
 
        /*
index a76f1bb7a2e44f4bee9cc3075ac1567bdd272401..ba3d82dd30756c176622c1d0439170409ceb98e0 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.37 2005/06/09 04:19:00 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.38 2005/07/02 23:00:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 static RestrictInfo *make_restrictinfo_internal(Expr *clause,
                                                   Expr *orclause,
                                                   bool is_pushed_down,
-                                                  bool valid_everywhere,
                                                   Relids required_relids);
 static Expr *make_sub_restrictinfos(Expr *clause,
-                                          bool is_pushed_down,
-                                          bool valid_everywhere);
+                                          bool is_pushed_down);
 static RestrictInfo *join_clause_is_redundant(PlannerInfo *root,
                                                 RestrictInfo *rinfo,
                                                 List *reference_list,
@@ -40,8 +38,8 @@ static RestrictInfo *join_clause_is_redundant(PlannerInfo *root,
  *
  * Build a RestrictInfo node containing the given subexpression.
  *
- * The is_pushed_down and valid_everywhere flags must be supplied by the
- * caller.  required_relids can be NULL, in which case it defaults to the
+ * The is_pushed_down flag must be supplied by the caller.
+ * required_relids can be NULL, in which case it defaults to the
  * actual clause contents (i.e., clause_relids).
  *
  * We initialize fields that depend only on the given subexpression, leaving
@@ -49,23 +47,19 @@ static RestrictInfo *join_clause_is_redundant(PlannerInfo *root,
  * later.
  */
 RestrictInfo *
-make_restrictinfo(Expr *clause, bool is_pushed_down, bool valid_everywhere,
-                                 Relids required_relids)
+make_restrictinfo(Expr *clause, bool is_pushed_down, Relids required_relids)
 {
        /*
         * If it's an OR clause, build a modified copy with RestrictInfos
         * inserted above each subclause of the top-level AND/OR structure.
         */
        if (or_clause((Node *) clause))
-               return (RestrictInfo *) make_sub_restrictinfos(clause,
-                                                                                                          is_pushed_down,
-                                                                                                          valid_everywhere);
+               return (RestrictInfo *) make_sub_restrictinfos(clause, is_pushed_down);
 
        /* Shouldn't be an AND clause, else AND/OR flattening messed up */
        Assert(!and_clause((Node *) clause));
 
-       return make_restrictinfo_internal(clause, NULL,
-                                                                         is_pushed_down, valid_everywhere,
+       return make_restrictinfo_internal(clause, NULL, is_pushed_down,
                                                                          required_relids);
 }
 
@@ -84,9 +78,7 @@ make_restrictinfo(Expr *clause, bool is_pushed_down, bool valid_everywhere,
  * a specialized routine to allow sharing of RestrictInfos.
  */
 List *
-make_restrictinfo_from_bitmapqual(Path *bitmapqual,
-                                                                 bool is_pushed_down,
-                                                                 bool valid_everywhere)
+make_restrictinfo_from_bitmapqual(Path *bitmapqual, bool is_pushed_down)
 {
        List       *result;
 
@@ -101,8 +93,7 @@ make_restrictinfo_from_bitmapqual(Path *bitmapqual,
                        List       *sublist;
 
                        sublist = make_restrictinfo_from_bitmapqual((Path *) lfirst(l),
-                                                                                                               is_pushed_down,
-                                                                                                               valid_everywhere);
+                                                                                                               is_pushed_down);
                        result = list_concat(result, sublist);
                }
        }
@@ -118,8 +109,7 @@ make_restrictinfo_from_bitmapqual(Path *bitmapqual,
                        List       *sublist;
 
                        sublist = make_restrictinfo_from_bitmapqual((Path *) lfirst(l),
-                                                                                                               is_pushed_down,
-                                                                                                               valid_everywhere);
+                                                                                                               is_pushed_down);
                        if (sublist == NIL)
                        {
                                /* constant TRUE input yields constant TRUE OR result */
@@ -137,7 +127,6 @@ make_restrictinfo_from_bitmapqual(Path *bitmapqual,
                        list_make1(make_restrictinfo_internal(make_orclause(withoutris),
                                                                                                  make_orclause(withris),
                                                                                                  is_pushed_down,
-                                                                                                 valid_everywhere,
                                                                                                  NULL));
        }
        else if (IsA(bitmapqual, IndexPath))
@@ -162,15 +151,13 @@ make_restrictinfo_from_bitmapqual(Path *bitmapqual,
  */
 static RestrictInfo *
 make_restrictinfo_internal(Expr *clause, Expr *orclause,
-                                                  bool is_pushed_down, bool valid_everywhere,
-                                                  Relids required_relids)
+                                                  bool is_pushed_down, Relids required_relids)
 {
        RestrictInfo *restrictinfo = makeNode(RestrictInfo);
 
        restrictinfo->clause = clause;
        restrictinfo->orclause = orclause;
        restrictinfo->is_pushed_down = is_pushed_down;
-       restrictinfo->valid_everywhere = valid_everywhere;
        restrictinfo->can_join = false;         /* may get set below */
 
        /*
@@ -250,8 +237,7 @@ make_restrictinfo_internal(Expr *clause, Expr *orclause,
  * simple clauses are valid RestrictInfos.
  */
 static Expr *
-make_sub_restrictinfos(Expr *clause, bool is_pushed_down,
-                                          bool valid_everywhere)
+make_sub_restrictinfos(Expr *clause, bool is_pushed_down)
 {
        if (or_clause((Node *) clause))
        {
@@ -261,12 +247,10 @@ make_sub_restrictinfos(Expr *clause, bool is_pushed_down,
                foreach(temp, ((BoolExpr *) clause)->args)
                        orlist = lappend(orlist,
                                                         make_sub_restrictinfos(lfirst(temp),
-                                                                                                       is_pushed_down,
-                                                                                                       valid_everywhere));
+                                                                                                       is_pushed_down));
                return (Expr *) make_restrictinfo_internal(clause,
                                                                                                   make_orclause(orlist),
                                                                                                   is_pushed_down,
-                                                                                                  valid_everywhere,
                                                                                                   NULL);
        }
        else if (and_clause((Node *) clause))
@@ -277,15 +261,13 @@ make_sub_restrictinfos(Expr *clause, bool is_pushed_down,
                foreach(temp, ((BoolExpr *) clause)->args)
                        andlist = lappend(andlist,
                                                          make_sub_restrictinfos(lfirst(temp),
-                                                                                                        is_pushed_down,
-                                                                                                        valid_everywhere));
+                                                                                                        is_pushed_down));
                return make_andclause(andlist);
        }
        else
                return (Expr *) make_restrictinfo_internal(clause,
                                                                                                   NULL,
                                                                                                   is_pushed_down,
-                                                                                                  valid_everywhere,
                                                                                                   NULL);
 }
 
index 0a2ca0e5f33fd957333a35299ceb8d6e8bc2723e..2f906c6a47297ac15a9b69a2ec2c679923023356 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.115 2005/06/13 23:14:49 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.116 2005/07/02 23:00:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -87,6 +87,15 @@ typedef struct PlannerInfo
        List       *equi_key_list;      /* list of lists of equijoined
                                                                 * PathKeyItems */
 
+       List       *left_join_clauses;  /* list of RestrictInfos for outer join
+                                                                        * clauses w/nonnullable var on left */
+
+       List       *right_join_clauses; /* list of RestrictInfos for outer join
+                                                                        * clauses w/nonnullable var on right */
+
+       List       *full_join_clauses;  /* list of RestrictInfos for full outer
+                                                                        * join clauses */
+
        List       *in_info_list;       /* list of InClauseInfos */
 
        List       *query_pathkeys; /* desired pathkeys for query_planner(),
@@ -95,6 +104,7 @@ typedef struct PlannerInfo
        double          tuple_fraction; /* tuple_fraction passed to query_planner */
 
        bool            hasJoinRTEs;    /* true if any RTEs are RTE_JOIN kind */
+       bool            hasOuterJoins;  /* true if any RTEs are outer joins */
        bool            hasHavingQual;  /* true if havingQual was non-null */
 } PlannerInfo;
 
@@ -695,10 +705,6 @@ typedef struct HashPath
  * joined, will also have is_pushed_down set because it will get attached to
  * some lower joinrel.
  *
- * We also store a valid_everywhere flag, which says that the clause is not
- * affected by any lower-level outer join, and therefore any conditions it
- * asserts can be presumed true throughout the plan tree.
- *
  * In general, the referenced clause might be arbitrarily complex.     The
  * kinds of clauses we can handle as indexscan quals, mergejoin clauses,
  * or hashjoin clauses are fairly limited --- the code for each kind of
@@ -725,8 +731,6 @@ typedef struct RestrictInfo
 
        bool            is_pushed_down; /* TRUE if clause was pushed down in level */
 
-       bool            valid_everywhere;               /* TRUE if valid on every level */
-
        /*
         * This flag is set true if the clause looks potentially useful as a
         * merge or hash join clause, that is if it is a binary opclause with
index 20f51c582045fe8fa560aec5455b5e1cd40758fc..5a9c2f2722488478ae3c951555441dec1ff9a3c9 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/restrictinfo.h,v 1.31 2005/06/09 04:19:00 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/restrictinfo.h,v 1.32 2005/07/02 23:00:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 extern RestrictInfo *make_restrictinfo(Expr *clause,
                                                                           bool is_pushed_down,
-                                                                          bool valid_everywhere,
                                                                           Relids required_relids);
 extern List *make_restrictinfo_from_bitmapqual(Path *bitmapqual,
-                                                                                          bool is_pushed_down,
-                                                                                          bool valid_everywhere);
+                                                                                          bool is_pushed_down);
 extern bool restriction_is_or_clause(RestrictInfo *restrictinfo);
 extern List *get_actual_clauses(List *restrictinfo_list);
 extern void get_actual_join_clauses(List *restrictinfo_list,