mirror of
https://github.com/postgres/postgres.git
synced 2025-07-07 00:36:50 +03:00
Disable support for partitionwise joins in problematic cases.
Commit f49842d
, which added support for partitionwise joins, built the
child's tlist by applying adjust_appendrel_attrs() to the parent's. So in
the case where the parent's included a whole-row Var for the parent, the
child's contained a ConvertRowtypeExpr. To cope with that, that commit
added code to the planner, such as setrefs.c, but some code paths still
assumed that the tlist for a scan (or join) rel would only include Vars
and PlaceHolderVars, which was true before that commit, causing errors:
* When creating an explicit sort node for an input path for a mergejoin
path for a child join, prepare_sort_from_pathkeys() threw the 'could not
find pathkey item to sort' error.
* When deparsing a relation participating in a pushed down child join as a
subquery in contrib/postgres_fdw, get_relation_column_alias_ids() threw
the 'unexpected expression in subquery output' error.
* When performing set_plan_references() on a local join plan generated by
contrib/postgres_fdw for EvalPlanQual support for a pushed down child
join, fix_join_expr() threw the 'variable not found in subplan target
lists' error.
To fix these, two approaches have been proposed: one by Ashutosh Bapat and
one by me. While the former keeps building the child's tlist with a
ConvertRowtypeExpr, the latter builds it with a whole-row Var for the
child not to violate the planner assumption, and tries to fix it up later,
But both approaches need more work, so refuse to generate partitionwise
join paths when whole-row Vars are involved, instead. We don't need to
handle ConvertRowtypeExprs in the child's tlists for now, so this commit
also removes the changes to the planner.
Previously, partitionwise join computed attr_needed data for each child
separately, and built the child join's tlist using that data, which also
required an extra step for adding PlaceHolderVars to that tlist, but it
would be more efficient to build it from the parent join's tlist through
the adjust_appendrel_attrs() transformation. So this commit builds that
list that way, and simplifies build_joinrel_tlist() and placeholder.c as
well as part of set_append_rel_size() to basically what they were before
partitionwise join went in.
Back-patch to PG11 where partitionwise join was introduced.
Report by Rajkumar Raghuwanshi. Analysis by Ashutosh Bapat, who also
provided some of regression tests. Patch by me, reviewed by Robert Haas.
Discussion: https://postgr.es/m/CAKcux6ktu-8tefLWtQuuZBYFaZA83vUzuRd7c1YHC-yEWyYFpg@mail.gmail.com
This commit is contained in:
@ -909,6 +909,17 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
|
||||
did_pruning = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this is a partitioned baserel, set the consider_partitionwise_join
|
||||
* flag; currently, we only consider partitionwise joins with the baserel
|
||||
* if its targetlist doesn't contain a whole-row Var.
|
||||
*/
|
||||
if (enable_partitionwise_join &&
|
||||
rel->reloptkind == RELOPT_BASEREL &&
|
||||
rte->relkind == RELKIND_PARTITIONED_TABLE &&
|
||||
rel->attr_needed[InvalidAttrNumber - rel->min_attr] == NULL)
|
||||
rel->consider_partitionwise_join = true;
|
||||
|
||||
/*
|
||||
* Initialize to compute size estimates for whole append relation.
|
||||
*
|
||||
@ -956,54 +967,6 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
|
||||
childrel = find_base_rel(root, childRTindex);
|
||||
Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
|
||||
|
||||
if (rel->part_scheme)
|
||||
{
|
||||
AttrNumber attno;
|
||||
|
||||
/*
|
||||
* We need attr_needed data for building targetlist of a join
|
||||
* relation representing join between matching partitions for
|
||||
* partitionwise join. A given attribute of a child will be needed
|
||||
* in the same highest joinrel where the corresponding attribute
|
||||
* of parent is needed. Hence it suffices to use the same Relids
|
||||
* set for parent and child.
|
||||
*/
|
||||
for (attno = rel->min_attr; attno <= rel->max_attr; attno++)
|
||||
{
|
||||
int index = attno - rel->min_attr;
|
||||
Relids attr_needed = rel->attr_needed[index];
|
||||
|
||||
/* System attributes do not need translation. */
|
||||
if (attno <= 0)
|
||||
{
|
||||
Assert(rel->min_attr == childrel->min_attr);
|
||||
childrel->attr_needed[index] = attr_needed;
|
||||
}
|
||||
else
|
||||
{
|
||||
Var *var = list_nth_node(Var,
|
||||
appinfo->translated_vars,
|
||||
attno - 1);
|
||||
int child_index;
|
||||
|
||||
/*
|
||||
* Ignore any column dropped from the parent.
|
||||
* Corresponding Var won't have any translation. It won't
|
||||
* have attr_needed information, since it can not be
|
||||
* referenced in the query.
|
||||
*/
|
||||
if (var == NULL)
|
||||
{
|
||||
Assert(attr_needed == NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
child_index = var->varattno - childrel->min_attr;
|
||||
childrel->attr_needed[child_index] = attr_needed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy/Modify targetlist. Even if this child is deemed empty, we need
|
||||
* its targetlist in case it falls on nullable side in a child-join
|
||||
@ -1180,6 +1143,22 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
|
||||
(Node *) rel->joininfo,
|
||||
1, &appinfo);
|
||||
|
||||
/*
|
||||
* Note: we could compute appropriate attr_needed data for the child's
|
||||
* variables, by transforming the parent's attr_needed through the
|
||||
* translated_vars mapping. However, currently there's no need
|
||||
* because attr_needed is only examined for base relations not
|
||||
* otherrels. So we just leave the child's attr_needed empty.
|
||||
*/
|
||||
|
||||
/*
|
||||
* If we consider partitionwise joins with the parent rel, do the same
|
||||
* for partitioned child rels.
|
||||
*/
|
||||
if (rel->consider_partitionwise_join &&
|
||||
childRTE->relkind == RELKIND_PARTITIONED_TABLE)
|
||||
childrel->consider_partitionwise_join = true;
|
||||
|
||||
/*
|
||||
* If parallelism is allowable for this query in general, see whether
|
||||
* it's allowable for this childrel in particular. But if we've
|
||||
@ -3538,6 +3517,9 @@ generate_partitionwise_join_paths(PlannerInfo *root, RelOptInfo *rel)
|
||||
if (!IS_PARTITIONED_REL(rel))
|
||||
return;
|
||||
|
||||
/* The relation should have consider_partitionwise_join set. */
|
||||
Assert(rel->consider_partitionwise_join);
|
||||
|
||||
/* Guard against stack overflow due to overly deep partition hierarchy. */
|
||||
check_stack_depth();
|
||||
|
||||
|
@ -1322,6 +1322,9 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
|
||||
if (!IS_PARTITIONED_REL(joinrel))
|
||||
return;
|
||||
|
||||
/* The join relation should have consider_partitionwise_join set. */
|
||||
Assert(joinrel->consider_partitionwise_join);
|
||||
|
||||
/*
|
||||
* Since this join relation is partitioned, all the base relations
|
||||
* participating in this join must be partitioned and so are all the
|
||||
@ -1330,6 +1333,10 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
|
||||
Assert(IS_PARTITIONED_REL(rel1) && IS_PARTITIONED_REL(rel2));
|
||||
Assert(REL_HAS_ALL_PART_PROPS(rel1) && REL_HAS_ALL_PART_PROPS(rel2));
|
||||
|
||||
/* The joining relations should have consider_partitionwise_join set. */
|
||||
Assert(rel1->consider_partitionwise_join &&
|
||||
rel2->consider_partitionwise_join);
|
||||
|
||||
/*
|
||||
* The partition scheme of the join relation should match that of the
|
||||
* joining relations.
|
||||
|
@ -41,9 +41,6 @@ typedef struct
|
||||
int num_vars; /* number of plain Var tlist entries */
|
||||
bool has_ph_vars; /* are there PlaceHolderVar entries? */
|
||||
bool has_non_vars; /* are there other entries? */
|
||||
bool has_conv_whole_rows; /* are there ConvertRowtypeExpr
|
||||
* entries encapsulating a whole-row
|
||||
* Var? */
|
||||
tlist_vinfo vars[FLEXIBLE_ARRAY_MEMBER]; /* has num_vars entries */
|
||||
} indexed_tlist;
|
||||
|
||||
@ -143,7 +140,6 @@ static List *set_returning_clause_references(PlannerInfo *root,
|
||||
int rtoffset);
|
||||
static bool extract_query_dependencies_walker(Node *node,
|
||||
PlannerInfo *context);
|
||||
static bool is_converted_whole_row_reference(Node *node);
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
@ -1997,7 +1993,6 @@ build_tlist_index(List *tlist)
|
||||
itlist->tlist = tlist;
|
||||
itlist->has_ph_vars = false;
|
||||
itlist->has_non_vars = false;
|
||||
itlist->has_conv_whole_rows = false;
|
||||
|
||||
/* Find the Vars and fill in the index array */
|
||||
vinfo = itlist->vars;
|
||||
@ -2016,8 +2011,6 @@ build_tlist_index(List *tlist)
|
||||
}
|
||||
else if (tle->expr && IsA(tle->expr, PlaceHolderVar))
|
||||
itlist->has_ph_vars = true;
|
||||
else if (is_converted_whole_row_reference((Node *) tle->expr))
|
||||
itlist->has_conv_whole_rows = true;
|
||||
else
|
||||
itlist->has_non_vars = true;
|
||||
}
|
||||
@ -2033,10 +2026,7 @@ build_tlist_index(List *tlist)
|
||||
* This is like build_tlist_index, but we only index tlist entries that
|
||||
* are Vars belonging to some rel other than the one specified. We will set
|
||||
* has_ph_vars (allowing PlaceHolderVars to be matched), but not has_non_vars
|
||||
* (so nothing other than Vars and PlaceHolderVars can be matched). In case of
|
||||
* DML, where this function will be used, returning lists from child relations
|
||||
* will be appended similar to a simple append relation. That does not require
|
||||
* fixing ConvertRowtypeExpr references. So, those are not considered here.
|
||||
* (so nothing other than Vars and PlaceHolderVars can be matched).
|
||||
*/
|
||||
static indexed_tlist *
|
||||
build_tlist_index_other_vars(List *tlist, Index ignore_rel)
|
||||
@ -2053,7 +2043,6 @@ build_tlist_index_other_vars(List *tlist, Index ignore_rel)
|
||||
itlist->tlist = tlist;
|
||||
itlist->has_ph_vars = false;
|
||||
itlist->has_non_vars = false;
|
||||
itlist->has_conv_whole_rows = false;
|
||||
|
||||
/* Find the desired Vars and fill in the index array */
|
||||
vinfo = itlist->vars;
|
||||
@ -2256,7 +2245,6 @@ static Node *
|
||||
fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
|
||||
{
|
||||
Var *newvar;
|
||||
bool converted_whole_row;
|
||||
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
@ -2326,12 +2314,8 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
|
||||
}
|
||||
if (IsA(node, Param))
|
||||
return fix_param_node(context->root, (Param *) node);
|
||||
|
||||
/* Try matching more complex expressions too, if tlists have any */
|
||||
converted_whole_row = is_converted_whole_row_reference(node);
|
||||
if (context->outer_itlist &&
|
||||
(context->outer_itlist->has_non_vars ||
|
||||
(context->outer_itlist->has_conv_whole_rows && converted_whole_row)))
|
||||
if (context->outer_itlist && context->outer_itlist->has_non_vars)
|
||||
{
|
||||
newvar = search_indexed_tlist_for_non_var((Expr *) node,
|
||||
context->outer_itlist,
|
||||
@ -2339,9 +2323,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
|
||||
if (newvar)
|
||||
return (Node *) newvar;
|
||||
}
|
||||
if (context->inner_itlist &&
|
||||
(context->inner_itlist->has_non_vars ||
|
||||
(context->inner_itlist->has_conv_whole_rows && converted_whole_row)))
|
||||
if (context->inner_itlist && context->inner_itlist->has_non_vars)
|
||||
{
|
||||
newvar = search_indexed_tlist_for_non_var((Expr *) node,
|
||||
context->inner_itlist,
|
||||
@ -2461,9 +2443,7 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
|
||||
/* If no match, just fall through to process it normally */
|
||||
}
|
||||
/* Try matching more complex expressions too, if tlist has any */
|
||||
if (context->subplan_itlist->has_non_vars ||
|
||||
(context->subplan_itlist->has_conv_whole_rows &&
|
||||
is_converted_whole_row_reference(node)))
|
||||
if (context->subplan_itlist->has_non_vars)
|
||||
{
|
||||
newvar = search_indexed_tlist_for_non_var((Expr *) node,
|
||||
context->subplan_itlist,
|
||||
@ -2670,33 +2650,3 @@ extract_query_dependencies_walker(Node *node, PlannerInfo *context)
|
||||
return expression_tree_walker(node, extract_query_dependencies_walker,
|
||||
(void *) context);
|
||||
}
|
||||
|
||||
/*
|
||||
* is_converted_whole_row_reference
|
||||
* If the given node is a ConvertRowtypeExpr encapsulating a whole-row
|
||||
* reference as implicit cast, return true. Otherwise return false.
|
||||
*/
|
||||
static bool
|
||||
is_converted_whole_row_reference(Node *node)
|
||||
{
|
||||
ConvertRowtypeExpr *convexpr;
|
||||
|
||||
if (!node || !IsA(node, ConvertRowtypeExpr))
|
||||
return false;
|
||||
|
||||
/* Traverse nested ConvertRowtypeExpr's. */
|
||||
convexpr = castNode(ConvertRowtypeExpr, node);
|
||||
while (convexpr->convertformat == COERCE_IMPLICIT_CAST &&
|
||||
IsA(convexpr->arg, ConvertRowtypeExpr))
|
||||
convexpr = castNode(ConvertRowtypeExpr, convexpr->arg);
|
||||
|
||||
if (IsA(convexpr->arg, Var))
|
||||
{
|
||||
Var *var = castNode(Var, convexpr->arg);
|
||||
|
||||
if (var->varattno == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include "optimizer/pathnode.h"
|
||||
#include "optimizer/placeholder.h"
|
||||
#include "optimizer/planmain.h"
|
||||
#include "optimizer/prep.h"
|
||||
#include "optimizer/var.h"
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
@ -415,10 +414,6 @@ add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
|
||||
Relids relids = joinrel->relids;
|
||||
ListCell *lc;
|
||||
|
||||
/* This function is called only on the parent relations. */
|
||||
Assert(!IS_OTHER_REL(joinrel) && !IS_OTHER_REL(outer_rel) &&
|
||||
!IS_OTHER_REL(inner_rel));
|
||||
|
||||
foreach(lc, root->placeholder_list)
|
||||
{
|
||||
PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
|
||||
@ -464,56 +459,3 @@ add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* add_placeholders_to_child_joinrel
|
||||
* Translate the PHVs in parent's targetlist and add them to the child's
|
||||
* targetlist. Also adjust the cost
|
||||
*/
|
||||
void
|
||||
add_placeholders_to_child_joinrel(PlannerInfo *root, RelOptInfo *childrel,
|
||||
RelOptInfo *parentrel)
|
||||
{
|
||||
ListCell *lc;
|
||||
AppendRelInfo **appinfos;
|
||||
int nappinfos;
|
||||
|
||||
Assert(IS_JOIN_REL(childrel) && IS_JOIN_REL(parentrel));
|
||||
Assert(IS_OTHER_REL(childrel));
|
||||
|
||||
/* Nothing to do if no PHVs. */
|
||||
if (root->placeholder_list == NIL)
|
||||
return;
|
||||
|
||||
appinfos = find_appinfos_by_relids(root, childrel->relids, &nappinfos);
|
||||
foreach(lc, parentrel->reltarget->exprs)
|
||||
{
|
||||
PlaceHolderVar *phv = lfirst(lc);
|
||||
|
||||
if (IsA(phv, PlaceHolderVar))
|
||||
{
|
||||
/*
|
||||
* In case the placeholder Var refers to any of the parent
|
||||
* relations, translate it to refer to the corresponding child.
|
||||
*/
|
||||
if (bms_overlap(phv->phrels, parentrel->relids) &&
|
||||
childrel->reloptkind == RELOPT_OTHER_JOINREL)
|
||||
{
|
||||
phv = (PlaceHolderVar *) adjust_appendrel_attrs(root,
|
||||
(Node *) phv,
|
||||
nappinfos,
|
||||
appinfos);
|
||||
}
|
||||
|
||||
childrel->reltarget->exprs = lappend(childrel->reltarget->exprs,
|
||||
phv);
|
||||
}
|
||||
}
|
||||
|
||||
/* Adjust the cost and width of child targetlist. */
|
||||
childrel->reltarget->cost.startup = parentrel->reltarget->cost.startup;
|
||||
childrel->reltarget->cost.per_tuple = parentrel->reltarget->cost.per_tuple;
|
||||
childrel->reltarget->width = parentrel->reltarget->width;
|
||||
|
||||
pfree(appinfos);
|
||||
}
|
||||
|
@ -57,6 +57,11 @@ static void add_join_rel(PlannerInfo *root, RelOptInfo *joinrel);
|
||||
static void build_joinrel_partition_info(RelOptInfo *joinrel,
|
||||
RelOptInfo *outer_rel, RelOptInfo *inner_rel,
|
||||
List *restrictlist, JoinType jointype);
|
||||
static void build_child_join_reltarget(PlannerInfo *root,
|
||||
RelOptInfo *parentrel,
|
||||
RelOptInfo *childrel,
|
||||
int nappinfos,
|
||||
AppendRelInfo **appinfos);
|
||||
|
||||
|
||||
/*
|
||||
@ -188,6 +193,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
|
||||
rel->baserestrict_min_security = UINT_MAX;
|
||||
rel->joininfo = NIL;
|
||||
rel->has_eclass_joins = false;
|
||||
rel->consider_partitionwise_join = false; /* might get changed later */
|
||||
rel->part_scheme = NULL;
|
||||
rel->nparts = 0;
|
||||
rel->boundinfo = NULL;
|
||||
@ -602,6 +608,7 @@ build_join_rel(PlannerInfo *root,
|
||||
joinrel->baserestrict_min_security = UINT_MAX;
|
||||
joinrel->joininfo = NIL;
|
||||
joinrel->has_eclass_joins = false;
|
||||
joinrel->consider_partitionwise_join = false; /* might get changed later */
|
||||
joinrel->top_parent_relids = NULL;
|
||||
joinrel->part_scheme = NULL;
|
||||
joinrel->nparts = 0;
|
||||
@ -732,6 +739,9 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
|
||||
/* Only joins between "other" relations land here. */
|
||||
Assert(IS_OTHER_REL(outer_rel) && IS_OTHER_REL(inner_rel));
|
||||
|
||||
/* The parent joinrel should have consider_partitionwise_join set. */
|
||||
Assert(parent_joinrel->consider_partitionwise_join);
|
||||
|
||||
joinrel->reloptkind = RELOPT_OTHER_JOINREL;
|
||||
joinrel->relids = bms_union(outer_rel->relids, inner_rel->relids);
|
||||
joinrel->rows = 0;
|
||||
@ -773,6 +783,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
|
||||
joinrel->baserestrictcost.per_tuple = 0;
|
||||
joinrel->joininfo = NIL;
|
||||
joinrel->has_eclass_joins = false;
|
||||
joinrel->consider_partitionwise_join = false; /* might get changed later */
|
||||
joinrel->top_parent_relids = NULL;
|
||||
joinrel->part_scheme = NULL;
|
||||
joinrel->nparts = 0;
|
||||
@ -789,14 +800,13 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
|
||||
/* Compute information relevant to foreign relations. */
|
||||
set_foreign_rel_properties(joinrel, outer_rel, inner_rel);
|
||||
|
||||
/* Build targetlist */
|
||||
build_joinrel_tlist(root, joinrel, outer_rel);
|
||||
build_joinrel_tlist(root, joinrel, inner_rel);
|
||||
/* Add placeholder variables. */
|
||||
add_placeholders_to_child_joinrel(root, joinrel, parent_joinrel);
|
||||
appinfos = find_appinfos_by_relids(root, joinrel->relids, &nappinfos);
|
||||
|
||||
/* Set up reltarget struct */
|
||||
build_child_join_reltarget(root, parent_joinrel, joinrel,
|
||||
nappinfos, appinfos);
|
||||
|
||||
/* Construct joininfo list. */
|
||||
appinfos = find_appinfos_by_relids(root, joinrel->relids, &nappinfos);
|
||||
joinrel->joininfo = (List *) adjust_appendrel_attrs(root,
|
||||
(Node *) parent_joinrel->joininfo,
|
||||
nappinfos,
|
||||
@ -826,7 +836,6 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
|
||||
/* Child joinrel is parallel safe if parent is parallel safe. */
|
||||
joinrel->consider_parallel = parent_joinrel->consider_parallel;
|
||||
|
||||
|
||||
/* Set estimates of the child-joinrel's size. */
|
||||
set_joinrel_size_estimates(root, joinrel, outer_rel, inner_rel,
|
||||
sjinfo, restrictlist);
|
||||
@ -895,15 +904,9 @@ static void
|
||||
build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
|
||||
RelOptInfo *input_rel)
|
||||
{
|
||||
Relids relids;
|
||||
Relids relids = joinrel->relids;
|
||||
ListCell *vars;
|
||||
|
||||
/* attrs_needed refers to parent relids and not those of a child. */
|
||||
if (joinrel->top_parent_relids)
|
||||
relids = joinrel->top_parent_relids;
|
||||
else
|
||||
relids = joinrel->relids;
|
||||
|
||||
foreach(vars, input_rel->reltarget->exprs)
|
||||
{
|
||||
Var *var = (Var *) lfirst(vars);
|
||||
@ -919,54 +922,23 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
|
||||
|
||||
/*
|
||||
* Otherwise, anything in a baserel or joinrel targetlist ought to be
|
||||
* a Var. Children of a partitioned table may have ConvertRowtypeExpr
|
||||
* translating whole-row Var of a child to that of the parent.
|
||||
* Children of an inherited table or subquery child rels can not
|
||||
* directly participate in a join, so other kinds of nodes here.
|
||||
* a Var. (More general cases can only appear in appendrel child
|
||||
* rels, which will never be seen here.)
|
||||
*/
|
||||
if (IsA(var, Var))
|
||||
{
|
||||
baserel = find_base_rel(root, var->varno);
|
||||
ndx = var->varattno - baserel->min_attr;
|
||||
}
|
||||
else if (IsA(var, ConvertRowtypeExpr))
|
||||
{
|
||||
ConvertRowtypeExpr *child_expr = (ConvertRowtypeExpr *) var;
|
||||
Var *childvar = (Var *) child_expr->arg;
|
||||
|
||||
/*
|
||||
* Child's whole-row references are converted to look like those
|
||||
* of parent using ConvertRowtypeExpr. There can be as many
|
||||
* ConvertRowtypeExpr decorations as the depth of partition tree.
|
||||
* The argument to the deepest ConvertRowtypeExpr is expected to
|
||||
* be a whole-row reference of the child.
|
||||
*/
|
||||
while (IsA(childvar, ConvertRowtypeExpr))
|
||||
{
|
||||
child_expr = (ConvertRowtypeExpr *) childvar;
|
||||
childvar = (Var *) child_expr->arg;
|
||||
}
|
||||
Assert(IsA(childvar, Var) &&childvar->varattno == 0);
|
||||
|
||||
baserel = find_base_rel(root, childvar->varno);
|
||||
ndx = 0 - baserel->min_attr;
|
||||
}
|
||||
else
|
||||
if (!IsA(var, Var))
|
||||
elog(ERROR, "unexpected node type in rel targetlist: %d",
|
||||
(int) nodeTag(var));
|
||||
|
||||
/* Get the Var's original base rel */
|
||||
baserel = find_base_rel(root, var->varno);
|
||||
|
||||
/* Is the target expression still needed above this joinrel? */
|
||||
/* Is it still needed above this joinrel? */
|
||||
ndx = var->varattno - baserel->min_attr;
|
||||
if (bms_nonempty_difference(baserel->attr_needed[ndx], relids))
|
||||
{
|
||||
/* Yup, add it to the output */
|
||||
joinrel->reltarget->exprs = lappend(joinrel->reltarget->exprs, var);
|
||||
|
||||
/*
|
||||
* Vars have cost zero, so no need to adjust reltarget->cost. Even
|
||||
* if it's a ConvertRowtypeExpr, it will be computed only for the
|
||||
* base relation, costing nothing for a join.
|
||||
*/
|
||||
/* Vars have cost zero, so no need to adjust reltarget->cost */
|
||||
joinrel->reltarget->width += baserel->attr_widths[ndx];
|
||||
}
|
||||
}
|
||||
@ -1626,16 +1598,18 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
|
||||
|
||||
/*
|
||||
* We can only consider this join as an input to further partitionwise
|
||||
* joins if (a) the input relations are partitioned, (b) the partition
|
||||
* schemes match, and (c) we can identify an equi-join between the
|
||||
* partition keys. Note that if it were possible for
|
||||
* have_partkey_equi_join to return different answers for the same joinrel
|
||||
* depending on which join ordering we try first, this logic would break.
|
||||
* That shouldn't happen, though, because of the way the query planner
|
||||
* deduces implied equalities and reorders the joins. Please see
|
||||
* optimizer/README for details.
|
||||
* joins if (a) the input relations are partitioned and have
|
||||
* consider_partitionwise_join=true, (b) the partition schemes match, and
|
||||
* (c) we can identify an equi-join between the partition keys. Note that
|
||||
* if it were possible for have_partkey_equi_join to return different
|
||||
* answers for the same joinrel depending on which join ordering we try
|
||||
* first, this logic would break. That shouldn't happen, though, because
|
||||
* of the way the query planner deduces implied equalities and reorders
|
||||
* the joins. Please see optimizer/README for details.
|
||||
*/
|
||||
if (!IS_PARTITIONED_REL(outer_rel) || !IS_PARTITIONED_REL(inner_rel) ||
|
||||
!outer_rel->consider_partitionwise_join ||
|
||||
!inner_rel->consider_partitionwise_join ||
|
||||
outer_rel->part_scheme != inner_rel->part_scheme ||
|
||||
!have_partkey_equi_join(joinrel, outer_rel, inner_rel,
|
||||
jointype, restrictlist))
|
||||
@ -1687,6 +1661,12 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
|
||||
joinrel->part_rels =
|
||||
(RelOptInfo **) palloc0(sizeof(RelOptInfo *) * joinrel->nparts);
|
||||
|
||||
/*
|
||||
* Set the consider_partitionwise_join flag.
|
||||
*/
|
||||
Assert(outer_rel->consider_partitionwise_join);
|
||||
Assert(inner_rel->consider_partitionwise_join);
|
||||
joinrel->consider_partitionwise_join = true;
|
||||
|
||||
/*
|
||||
* Construct partition keys for the join.
|
||||
@ -1768,3 +1748,26 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
|
||||
joinrel->nullable_partexprs[cnt] = nullable_partexpr;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* build_child_join_reltarget
|
||||
* Set up a child-join relation's reltarget from a parent-join relation.
|
||||
*/
|
||||
static void
|
||||
build_child_join_reltarget(PlannerInfo *root,
|
||||
RelOptInfo *parentrel,
|
||||
RelOptInfo *childrel,
|
||||
int nappinfos,
|
||||
AppendRelInfo **appinfos)
|
||||
{
|
||||
/* Build the targetlist */
|
||||
childrel->reltarget->exprs = (List *)
|
||||
adjust_appendrel_attrs(root,
|
||||
(Node *) parentrel->reltarget->exprs,
|
||||
nappinfos, appinfos);
|
||||
|
||||
/* Set the cost and width fields */
|
||||
childrel->reltarget->cost.startup = parentrel->reltarget->cost.startup;
|
||||
childrel->reltarget->cost.per_tuple = parentrel->reltarget->cost.per_tuple;
|
||||
childrel->reltarget->width = parentrel->reltarget->width;
|
||||
}
|
||||
|
Reference in New Issue
Block a user