1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-30 21:42:05 +03:00

Basic partition-wise join functionality.

Instead of joining two partitioned tables in their entirety we can, if
it is an equi-join on the partition keys, join the matching partitions
individually.  This involves teaching the planner about "other join"
rels, which are related to regular join rels in the same way that
other member rels are related to baserels.  This can use significantly
more CPU time and memory than regular join planning, because there may
now be a set of "other" rels not only for every base relation but also
for every join relation.  In most practical cases, this probably
shouldn't be a problem, because (1) it's probably unusual to join many
tables each with many partitions using the partition keys for all
joins and (2) if you do that scenario then you probably have a big
enough machine to handle the increased memory cost of planning and (3)
the resulting plan is highly likely to be better, so what you spend in
planning you'll make up on the execution side.  All the same, for now,
turn this feature off by default.

Currently, we can only perform joins between two tables whose
partitioning schemes are absolutely identical.  It would be nice to
cope with other scenarios, such as extra partitions on one side or the
other with no match on the other side, but that will have to wait for
a future patch.

Ashutosh Bapat, reviewed and tested by Rajkumar Raghuwanshi, Amit
Langote, Rafia Sabih, Thomas Munro, Dilip Kumar, Antonin Houska, Amit
Khandekar, and by me.  A few final adjustments by me.

Discussion: http://postgr.es/m/CAFjFpRfQ8GrQvzp3jA2wnLqrHmaXna-urjm_UY9BqXj=EaDTSA@mail.gmail.com
Discussion: http://postgr.es/m/CAFjFpRcitjfrULr5jfuKWRPsGUX0LQ0k8-yG0Qw2+1LBGNpMdw@mail.gmail.com
This commit is contained in:
Robert Haas
2017-10-06 11:11:10 -04:00
parent fe9ba28ee8
commit f49842d1ee
34 changed files with 4089 additions and 140 deletions

View File

@ -41,6 +41,9 @@ 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;
@ -139,6 +142,7 @@ 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);
/*****************************************************************************
*
@ -1944,6 +1948,7 @@ 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;
@ -1962,6 +1967,8 @@ 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;
}
@ -1977,7 +1984,10 @@ 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).
* (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.
*/
static indexed_tlist *
build_tlist_index_other_vars(List *tlist, Index ignore_rel)
@ -1994,6 +2004,7 @@ 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;
@ -2197,6 +2208,7 @@ static Node *
fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
{
Var *newvar;
bool converted_whole_row;
if (node == NULL)
return NULL;
@ -2266,8 +2278,12 @@ 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 */
if (context->outer_itlist && context->outer_itlist->has_non_vars)
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)))
{
newvar = search_indexed_tlist_for_non_var((Expr *) node,
context->outer_itlist,
@ -2275,7 +2291,9 @@ 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)
if (context->inner_itlist &&
(context->inner_itlist->has_non_vars ||
(context->inner_itlist->has_conv_whole_rows && converted_whole_row)))
{
newvar = search_indexed_tlist_for_non_var((Expr *) node,
context->inner_itlist,
@ -2395,7 +2413,9 @@ 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)
if (context->subplan_itlist->has_non_vars ||
(context->subplan_itlist->has_conv_whole_rows &&
is_converted_whole_row_reference(node)))
{
newvar = search_indexed_tlist_for_non_var((Expr *) node,
context->subplan_itlist,
@ -2602,3 +2622,33 @@ 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;
}