diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 859280686c2..b1e69b7b3a5 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.91.2.2 2004/02/27 21:42:10 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.91.2.3 2005/09/28 21:17:48 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -38,6 +38,7 @@ static void mark_baserels_for_outer_join(Query *root, Relids rels, static void distribute_qual_to_rels(Query *root, Node *clause, bool ispusheddown, bool isdeduced, + bool below_outer_join, Relids outerjoin_nonnullable, Relids qualscope); static void add_vars_to_targetlist(Query *root, List *vars, @@ -173,6 +174,10 @@ add_vars_to_targetlist(Query *root, List *vars, Relids where_needed) * with outerjoinset information, to aid in proper positioning of qual * clauses that appear above outer joins. * + * jtnode is the jointree node currently being examined. below_outer_join + * is TRUE if this node is within the nullable side of a higher-level outer + * join. + * * NOTE: when dealing with inner joins, it is appropriate to let a qual clause * be evaluated at the lowest level where all the variables it mentions are * available. However, we cannot push a qual down into the nullable side(s) @@ -188,7 +193,8 @@ add_vars_to_targetlist(Query *root, List *vars, Relids where_needed) * internal convenience; no outside callers pay attention to the result. */ Relids -distribute_quals_to_rels(Query *root, Node *jtnode) +distribute_quals_to_rels(Query *root, Node *jtnode, + bool below_outer_join) { Relids result = NULL; @@ -214,7 +220,8 @@ distribute_quals_to_rels(Query *root, Node *jtnode) { result = bms_add_members(result, distribute_quals_to_rels(root, - lfirst(l))); + lfirst(l), + below_outer_join)); } /* @@ -223,7 +230,8 @@ distribute_quals_to_rels(Query *root, Node *jtnode) */ foreach(qual, (List *) f->quals) distribute_qual_to_rels(root, (Node *) lfirst(qual), - true, false, NULL, result); + true, false, below_outer_join, + NULL, result); } else if (IsA(jtnode, JoinExpr)) { @@ -247,27 +255,47 @@ distribute_quals_to_rels(Query *root, Node *jtnode) * rels from being pushed down below this level. (It's okay for * upper quals to be pushed down to the outer side, however.) */ - leftids = distribute_quals_to_rels(root, j->larg); - rightids = distribute_quals_to_rels(root, j->rarg); - - result = bms_union(leftids, rightids); - - nonnullable_rels = nullable_rels = NULL; switch (j->jointype) { case JOIN_INNER: + leftids = distribute_quals_to_rels(root, j->larg, + below_outer_join); + rightids = distribute_quals_to_rels(root, j->rarg, + below_outer_join); + + result = bms_union(leftids, rightids); /* Inner join adds no restrictions for quals */ + nonnullable_rels = NULL; + nullable_rels = NULL; break; case JOIN_LEFT: + leftids = distribute_quals_to_rels(root, j->larg, + below_outer_join); + rightids = distribute_quals_to_rels(root, j->rarg, + true); + + result = bms_union(leftids, rightids); nonnullable_rels = leftids; nullable_rels = rightids; break; case JOIN_FULL: + leftids = distribute_quals_to_rels(root, j->larg, + true); + rightids = distribute_quals_to_rels(root, j->rarg, + true); + + result = bms_union(leftids, rightids); /* each side is both outer and inner */ nonnullable_rels = result; nullable_rels = result; break; case JOIN_RIGHT: + leftids = distribute_quals_to_rels(root, j->larg, + true); + rightids = distribute_quals_to_rels(root, j->rarg, + below_outer_join); + + result = bms_union(leftids, rightids); nonnullable_rels = rightids; nullable_rels = leftids; break; @@ -280,16 +308,20 @@ distribute_quals_to_rels(Query *root, Node *jtnode) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("UNION JOIN is not implemented"))); + nonnullable_rels = NULL; /* keep compiler quiet */ + nullable_rels = NULL; break; default: elog(ERROR, "unrecognized join type: %d", (int) j->jointype); + nonnullable_rels = NULL; /* keep compiler quiet */ + nullable_rels = NULL; break; } foreach(qual, (List *) j->quals) distribute_qual_to_rels(root, (Node *) lfirst(qual), - false, false, + false, false, below_outer_join, nonnullable_rels, result); if (nullable_rels != NULL) @@ -358,6 +390,8 @@ mark_baserels_for_outer_join(Query *root, Relids rels, Relids outerrels) * 'ispusheddown': if TRUE, force the clause to be marked 'ispusheddown' * (this indicates the clause came from a FromExpr, not a JoinExpr) * 'isdeduced': TRUE if the qual came from implied-equality deduction + * 'below_outer_join': TRUE if the qual is from a JOIN/ON that is below the + * nullable side of a higher-level outer join. * 'outerjoin_nonnullable': NULL if not an outer-join qual, else the set of * baserels appearing on the outer (nonnullable) side of the join * 'qualscope': set of baserels the qual's syntactic scope covers @@ -370,6 +404,7 @@ static void distribute_qual_to_rels(Query *root, Node *clause, bool ispusheddown, bool isdeduced, + bool below_outer_join, Relids outerjoin_nonnullable, Relids qualscope) { @@ -448,7 +483,8 @@ distribute_qual_to_rels(Query *root, Node *clause, * * Note: an outer-join qual that mentions only nullable-side rels can * be pushed down into the nullable side without changing the join - * result, so we treat it the same as an ordinary inner-join qual. + * result, so we treat it the same as an ordinary inner-join qual, + * except for not setting can_be_equijoin (see below). */ relids = qualscope; can_be_equijoin = false; @@ -482,8 +518,19 @@ distribute_qual_to_rels(Query *root, Node *clause, if (bms_is_subset(addrelids, relids)) { - /* Qual is not affected by any outer-join restriction */ - can_be_equijoin = true; + /* + * Qual is not delayed by any lower outer-join restriction. + * If it is not itself below or within an outer join, we + * can consider it "valid everywhere", so consider feeding + * it to the equijoin machinery. (If it is within an outer + * join, we can't consider it "valid everywhere": once the + * contained variables have gone to NULL, we'd be asserting + * things like NULL = NULL, which is not true.) + */ + if (!below_outer_join && outerjoin_nonnullable == NULL) + can_be_equijoin = true; + else + can_be_equijoin = false; } else { @@ -788,7 +835,7 @@ process_implied_equality(Query *root, * taken for an original JOIN/ON clause. */ distribute_qual_to_rels(root, (Node *) clause, - true, true, NULL, relids); + true, true, false, NULL, relids); } /* diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index 287c9e656ef..a8159dd972b 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.78 2003/08/04 02:40:01 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.78.4.1 2005/09/28 21:17:49 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -133,7 +133,7 @@ query_planner(Query *root, List *tlist, double tuple_fraction, */ build_base_rel_tlists(root, tlist); - (void) distribute_quals_to_rels(root, (Node *) root->jointree); + (void) distribute_quals_to_rels(root, (Node *) root->jointree, false); /* * Use the completed lists of equijoined keys to deduce any implied diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index a27cc9ebc60..d288c4e3374 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: planmain.h,v 1.75 2003/08/08 21:42:50 momjian Exp $ + * $Id: planmain.h,v 1.75.4.1 2005/09/28 21:17:50 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -57,7 +57,8 @@ extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan); */ extern void add_base_rels_to_query(Query *root, Node *jtnode); extern void build_base_rel_tlists(Query *root, List *final_tlist); -extern Relids distribute_quals_to_rels(Query *root, Node *jtnode); +extern Relids distribute_quals_to_rels(Query *root, Node *jtnode, + bool below_outer_join); extern void process_implied_equality(Query *root, Node *item1, Node *item2, Oid sortop1, Oid sortop2,