mirror of
https://github.com/postgres/postgres.git
synced 2025-10-29 22:49:41 +03:00
Fix up planner infrastructure to support LATERAL properly.
This patch takes care of a number of problems having to do with failure to choose valid join orders and incorrect handling of lateral references pulled up from subqueries. Notable changes: * Add a LateralJoinInfo data structure similar to SpecialJoinInfo, to represent join ordering constraints created by lateral references. (I first considered extending the SpecialJoinInfo structure, but the semantics are different enough that a separate data structure seems better.) Extend join_is_legal() and related functions to prevent trying to form unworkable joins, and to ensure that we will consider joins that satisfy lateral references even if the joins would be clauseless. * Fill in the infrastructure needed for the last few types of relation scan paths to support parameterization. We'd have wanted this eventually anyway, but it is necessary now because a relation that gets pulled up out of a UNION ALL subquery may acquire a reltargetlist containing lateral references, meaning that its paths *have* to be parameterized whether or not we have any code that can push join quals down into the scan. * Compute data about lateral references early in query_planner(), and save in RelOptInfo nodes, to avoid repetitive calculations later. * Assorted corner-case bug fixes. There's probably still some bugs left, but this is a lot closer to being real than it was before.
This commit is contained in:
@@ -54,6 +54,7 @@ planner_hook_type planner_hook = NULL;
|
||||
#define EXPRKIND_VALUES 3
|
||||
#define EXPRKIND_LIMIT 4
|
||||
#define EXPRKIND_APPINFO 5
|
||||
#define EXPRKIND_PHV 6
|
||||
|
||||
|
||||
static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind);
|
||||
@@ -348,10 +349,12 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
|
||||
/*
|
||||
* 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 pull_up_subqueries, of course.
|
||||
* outer joins --- if none, we can skip reduce_outer_joins(). And check
|
||||
* for LATERAL RTEs, too. This must be done after we have done
|
||||
* pull_up_subqueries(), of course.
|
||||
*/
|
||||
root->hasJoinRTEs = false;
|
||||
root->hasLateralRTEs = false;
|
||||
hasOuterJoins = false;
|
||||
foreach(l, parse->rtable)
|
||||
{
|
||||
@@ -361,12 +364,10 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
|
||||
{
|
||||
root->hasJoinRTEs = true;
|
||||
if (IS_OUTER_JOIN(rte->jointype))
|
||||
{
|
||||
hasOuterJoins = true;
|
||||
/* Can quit scanning once we find an outer join */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rte->lateral)
|
||||
root->hasLateralRTEs = true;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -576,7 +577,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
|
||||
* preprocess_expression
|
||||
* Do subquery_planner's preprocessing work for an expression,
|
||||
* which can be a targetlist, a WHERE clause (including JOIN/ON
|
||||
* conditions), or a HAVING clause.
|
||||
* conditions), a HAVING clause, or a few other things.
|
||||
*/
|
||||
static Node *
|
||||
preprocess_expression(PlannerInfo *root, Node *expr, int kind)
|
||||
@@ -692,6 +693,23 @@ preprocess_qual_conditions(PlannerInfo *root, Node *jtnode)
|
||||
(int) nodeTag(jtnode));
|
||||
}
|
||||
|
||||
/*
|
||||
* preprocess_phv_expression
|
||||
* Do preprocessing on a PlaceHolderVar expression that's been pulled up.
|
||||
*
|
||||
* If a LATERAL subquery references an output of another subquery, and that
|
||||
* output must be wrapped in a PlaceHolderVar because of an intermediate outer
|
||||
* join, then we'll push the PlaceHolderVar expression down into the subquery
|
||||
* and later pull it back up during find_lateral_references, which runs after
|
||||
* subquery_planner has preprocessed all the expressions that were in the
|
||||
* current query level to start with. So we need to preprocess it then.
|
||||
*/
|
||||
Expr *
|
||||
preprocess_phv_expression(PlannerInfo *root, Expr *expr)
|
||||
{
|
||||
return (Expr *) preprocess_expression(root, (Node *) expr, EXPRKIND_PHV);
|
||||
}
|
||||
|
||||
/*
|
||||
* inheritance_planner
|
||||
* Generate a plan in the case where the result relation is an
|
||||
@@ -821,8 +839,9 @@ inheritance_planner(PlannerInfo *root)
|
||||
}
|
||||
|
||||
/* We needn't modify the child's append_rel_list */
|
||||
/* There shouldn't be any OJ info to translate, as yet */
|
||||
/* There shouldn't be any OJ or LATERAL info to translate, as yet */
|
||||
Assert(subroot.join_info_list == NIL);
|
||||
Assert(subroot.lateral_info_list == NIL);
|
||||
/* and we haven't created PlaceHolderInfos, either */
|
||||
Assert(subroot.placeholder_list == NIL);
|
||||
/* hack to mark target relation as an inheritance partition */
|
||||
|
||||
Reference in New Issue
Block a user