提交 2e1254e7 编写于 作者: T Tom Lane

Repair planning bug introduced in 7.4: outer-join ON clauses that referenced

only the inner-side relation would be considered as potential equijoin clauses,
which is wrong because the condition doesn't necessarily hold above the point
of the outer join.  Per test case from Kevin Grittner (bug#1916).
上级 4ff20326
......@@ -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);
}
/*
......
......@@ -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
......
......@@ -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,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册