diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 91a23d6d1c3..7e3d5bca55b 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.108 2005/07/02 23:00:41 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.109 2005/09/28 21:17:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -38,7 +38,8 @@ static void mark_baserels_for_outer_join(PlannerInfo *root, Relids rels, Relids outerrels); static void distribute_qual_to_rels(PlannerInfo *root, Node *clause, bool is_pushed_down, - bool isdeduced, + bool is_deduced, + bool below_outer_join, Relids outerjoin_nonnullable, Relids qualscope); static void add_vars_to_targetlist(PlannerInfo *root, List *vars, @@ -174,6 +175,10 @@ add_vars_to_targetlist(PlannerInfo *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) @@ -189,7 +194,8 @@ add_vars_to_targetlist(PlannerInfo *root, List *vars, Relids where_needed) * internal convenience; no outside callers pay attention to the result. */ Relids -distribute_quals_to_rels(PlannerInfo *root, Node *jtnode) +distribute_quals_to_rels(PlannerInfo *root, Node *jtnode, + bool below_outer_join) { Relids result = NULL; @@ -214,7 +220,8 @@ distribute_quals_to_rels(PlannerInfo *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(PlannerInfo *root, Node *jtnode) */ foreach(l, (List *) f->quals) distribute_qual_to_rels(root, (Node *) lfirst(l), - true, false, NULL, result); + true, false, below_outer_join, + NULL, result); } else if (IsA(jtnode, JoinExpr)) { @@ -247,27 +255,47 @@ distribute_quals_to_rels(PlannerInfo *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(PlannerInfo *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) @@ -357,7 +389,9 @@ mark_baserels_for_outer_join(PlannerInfo *root, Relids rels, Relids outerrels) * 'clause': the qual clause to be distributed * 'is_pushed_down': if TRUE, force the clause to be marked 'is_pushed_down' * (this indicates the clause came from a FromExpr, not a JoinExpr) - * 'isdeduced': TRUE if the qual came from implied-equality deduction + * 'is_deduced': 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 @@ -369,7 +403,8 @@ mark_baserels_for_outer_join(PlannerInfo *root, Relids rels, Relids outerrels) static void distribute_qual_to_rels(PlannerInfo *root, Node *clause, bool is_pushed_down, - bool isdeduced, + bool is_deduced, + bool below_outer_join, Relids outerjoin_nonnullable, Relids qualscope) { @@ -406,7 +441,7 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, * Check to see if clause application must be delayed by outer-join * considerations. */ - if (isdeduced) + if (is_deduced) { /* * If the qual came from implied-equality deduction, we always @@ -432,14 +467,18 @@ distribute_qual_to_rels(PlannerInfo *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 maybe_equijoin (see below). */ relids = qualscope; /* * 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. + * for more limited purposes. Note: for the current uses of + * deductions from an outer-join clause, it seems safe to make + * the deductions even when the clause is below a higher-level + * outer join; so we do not check below_outer_join here. */ maybe_equijoin = false; maybe_outer_join = true; @@ -473,8 +512,19 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, if (bms_is_subset(addrelids, relids)) { - /* Qual is not affected by any outer-join restriction */ - maybe_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) + maybe_equijoin = true; + else + maybe_equijoin = false; } else { @@ -543,7 +593,7 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, * redundancy will be detected when the join clause is moved * into a join rel's restriction list.) */ - if (!isdeduced || + if (!is_deduced || !qual_is_redundant(root, restrictinfo, rel->baserestrictinfo)) { @@ -810,7 +860,7 @@ process_implied_equality(PlannerInfo *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 1aca1249d43..24d53be9e97 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.87 2005/08/27 22:13:43 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.88 2005/09/28 21:17:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -155,7 +155,7 @@ query_planner(PlannerInfo *root, List *tlist, double tuple_fraction, */ build_base_rel_tlists(root, tlist); - (void) distribute_quals_to_rels(root, (Node *) parse->jointree); + (void) distribute_quals_to_rels(root, (Node *) parse->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 652431b894f..59b925f8b74 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -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/planmain.h,v 1.88 2005/08/27 22:13:44 tgl Exp $ + * $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.89 2005/09/28 21:17:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -67,7 +67,8 @@ extern bool is_projection_capable_plan(Plan *plan); */ extern void add_base_rels_to_query(PlannerInfo *root, Node *jtnode); extern void build_base_rel_tlists(PlannerInfo *root, List *final_tlist); -extern Relids distribute_quals_to_rels(PlannerInfo *root, Node *jtnode); +extern Relids distribute_quals_to_rels(PlannerInfo *root, Node *jtnode, + bool below_outer_join); extern void process_implied_equality(PlannerInfo *root, Node *item1, Node *item2, Oid sortop1, Oid sortop2,