mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-12-27 08:39:28 +08:00
Resurrect the "last ditch" code path in join_search_one_level().
This essentially reverts commit e54b10a62d
,
in which I'd decided that the "last ditch" join logic was useless. The
folly of that is now exposed by a report from Pavel Stehule: although the
function should always find at least one join in a self-contained join
problem, it can still fail to do so in a sub-problem created by artificial
from_collapse_limit or join_collapse_limit constraints. Adjust the
comments to describe this, and simplify the code a bit to match the new
coding of the earlier loop in the function.
I'm not terribly happy about this: I still subscribe to the opinion stated
in the previous commit message that the "last ditch" code can obscure logic
bugs elsewhere. But the alternative seems to be to complicate the earlier
tests for does-this-relation-have-a-join-clause to the point where they can
tell whether the join clauses link outside the current join sub-problem.
And that looks messy, slow, and possibly a source of bugs in itself.
In any case, now is not the time to be inserting experimental code into
9.2, so let's just go back to the time-tested solution.
This commit is contained in:
parent
864de654c8
commit
eb919e8fde
@ -178,25 +178,59 @@ join_search_one_level(PlannerInfo *root, int level)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*----------
|
/*----------
|
||||||
* Normally, we should always have made at least one join of the current
|
* Last-ditch effort: if we failed to find any usable joins so far, force
|
||||||
* level. However, when special joins are involved, there may be no legal
|
* a set of cartesian-product joins to be generated. This handles the
|
||||||
* way to make an N-way join for some values of N. For example consider
|
* special case where all the available rels have join clauses but we
|
||||||
|
* cannot use any of those clauses yet. This can only happen when we are
|
||||||
|
* considering a join sub-problem (a sub-joinlist) and all the rels in the
|
||||||
|
* sub-problem have only join clauses with rels outside the sub-problem.
|
||||||
|
* An example is
|
||||||
*
|
*
|
||||||
* SELECT ... FROM t1 WHERE
|
* SELECT ... FROM a INNER JOIN b ON TRUE, c, d, ...
|
||||||
* x IN (SELECT ... FROM t2,t3 WHERE ...) AND
|
* WHERE a.w = c.x and b.y = d.z;
|
||||||
* y IN (SELECT ... FROM t4,t5 WHERE ...)
|
|
||||||
*
|
*
|
||||||
* We will flatten this query to a 5-way join problem, but there are
|
* If the "a INNER JOIN b" sub-problem does not get flattened into the
|
||||||
* no 4-way joins that join_is_legal() will consider legal. We have
|
* upper level, we must be willing to make a cartesian join of a and b;
|
||||||
* to accept failure at level 4 and go on to discover a workable
|
* but the code above will not have done so, because it thought that both
|
||||||
* bushy plan at level 5.
|
* a and b have joinclauses. We consider only left-sided and right-sided
|
||||||
*
|
* cartesian joins in this case (no bushy).
|
||||||
* However, if there are no special joins then join_is_legal() should
|
|
||||||
* never fail, and so the following sanity check is useful.
|
|
||||||
*----------
|
*----------
|
||||||
*/
|
*/
|
||||||
if (joinrels[level] == NIL && root->join_info_list == NIL)
|
if (joinrels[level] == NIL)
|
||||||
elog(ERROR, "failed to build any %d-way joins", level);
|
{
|
||||||
|
/*
|
||||||
|
* This loop is just like the first one, except we always call
|
||||||
|
* make_rels_by_clauseless_joins().
|
||||||
|
*/
|
||||||
|
foreach(r, joinrels[level - 1])
|
||||||
|
{
|
||||||
|
RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
|
||||||
|
|
||||||
|
make_rels_by_clauseless_joins(root,
|
||||||
|
old_rel,
|
||||||
|
list_head(joinrels[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------
|
||||||
|
* When special joins are involved, there may be no legal way
|
||||||
|
* to make an N-way join for some values of N. For example consider
|
||||||
|
*
|
||||||
|
* SELECT ... FROM t1 WHERE
|
||||||
|
* x IN (SELECT ... FROM t2,t3 WHERE ...) AND
|
||||||
|
* y IN (SELECT ... FROM t4,t5 WHERE ...)
|
||||||
|
*
|
||||||
|
* We will flatten this query to a 5-way join problem, but there are
|
||||||
|
* no 4-way joins that join_is_legal() will consider legal. We have
|
||||||
|
* to accept failure at level 4 and go on to discover a workable
|
||||||
|
* bushy plan at level 5.
|
||||||
|
*
|
||||||
|
* However, if there are no special joins then join_is_legal() should
|
||||||
|
* never fail, and so the following sanity check is useful.
|
||||||
|
*----------
|
||||||
|
*/
|
||||||
|
if (joinrels[level] == NIL && root->join_info_list == NIL)
|
||||||
|
elog(ERROR, "failed to build any %d-way joins", level);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -724,6 +758,13 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
|
|||||||
* could be merged with that function, but it seems clearer to separate the
|
* could be merged with that function, but it seems clearer to separate the
|
||||||
* two concerns. We need this test because there are degenerate cases where
|
* two concerns. We need this test because there are degenerate cases where
|
||||||
* a clauseless join must be performed to satisfy join-order restrictions.
|
* a clauseless join must be performed to satisfy join-order restrictions.
|
||||||
|
*
|
||||||
|
* Note: this is only a problem if one side of a degenerate outer join
|
||||||
|
* contains multiple rels, or a clauseless join is required within an
|
||||||
|
* IN/EXISTS RHS; else we will find a join path via the "last ditch" case in
|
||||||
|
* join_search_one_level(). We could dispense with this test if we were
|
||||||
|
* willing to try bushy plans in the "last ditch" case, but that seems much
|
||||||
|
* less efficient.
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
have_join_order_restriction(PlannerInfo *root,
|
have_join_order_restriction(PlannerInfo *root,
|
||||||
|
Loading…
Reference in New Issue
Block a user