1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-03 20:02:46 +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

@ -18,15 +18,20 @@
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "nodes/extensible.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
#include "optimizer/prep.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "parser/parsetree.h"
#include "foreign/fdwapi.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/selfuncs.h"
@ -46,6 +51,9 @@ typedef enum
#define STD_FUZZ_FACTOR 1.01
static List *translate_sub_tlist(List *tlist, int relid);
static List *reparameterize_pathlist_by_child(PlannerInfo *root,
List *pathlist,
RelOptInfo *child_rel);
/*****************************************************************************
@ -3429,3 +3437,358 @@ reparameterize_path(PlannerInfo *root, Path *path,
}
return NULL;
}
/*
* reparameterize_path_by_child
* Given a path parameterized by the parent of the given child relation,
* translate the path to be parameterized by the given child relation.
*
* The function creates a new path of the same type as the given path, but
* parameterized by the given child relation. Most fields from the original
* path can simply be flat-copied, but any expressions must be adjusted to
* refer to the correct varnos, and any paths must be recursively
* reparameterized. Other fields that refer to specific relids also need
* adjustment.
*
* The cost, number of rows, width and parallel path properties depend upon
* path->parent, which does not change during the translation. Hence those
* members are copied as they are.
*
* If the given path can not be reparameterized, the function returns NULL.
*/
Path *
reparameterize_path_by_child(PlannerInfo *root, Path *path,
RelOptInfo *child_rel)
{
#define FLAT_COPY_PATH(newnode, node, nodetype) \
( (newnode) = makeNode(nodetype), \
memcpy((newnode), (node), sizeof(nodetype)) )
#define ADJUST_CHILD_ATTRS(node) \
((node) = \
(List *) adjust_appendrel_attrs_multilevel(root, (Node *) (node), \
child_rel->relids, \
child_rel->top_parent_relids))
#define REPARAMETERIZE_CHILD_PATH(path) \
do { \
(path) = reparameterize_path_by_child(root, (path), child_rel); \
if ((path) == NULL) \
return NULL; \
} while(0);
#define REPARAMETERIZE_CHILD_PATH_LIST(pathlist) \
do { \
if ((pathlist) != NIL) \
{ \
(pathlist) = reparameterize_pathlist_by_child(root, (pathlist), \
child_rel); \
if ((pathlist) == NIL) \
return NULL; \
} \
} while(0);
Path *new_path;
ParamPathInfo *new_ppi;
ParamPathInfo *old_ppi;
Relids required_outer;
/*
* If the path is not parameterized by parent of the given relation, it
* doesn't need reparameterization.
*/
if (!path->param_info ||
!bms_overlap(PATH_REQ_OUTER(path), child_rel->top_parent_relids))
return path;
/* Reparameterize a copy of given path. */
switch (nodeTag(path))
{
case T_Path:
FLAT_COPY_PATH(new_path, path, Path);
break;
case T_IndexPath:
{
IndexPath *ipath;
FLAT_COPY_PATH(ipath, path, IndexPath);
ADJUST_CHILD_ATTRS(ipath->indexclauses);
ADJUST_CHILD_ATTRS(ipath->indexquals);
new_path = (Path *) ipath;
}
break;
case T_BitmapHeapPath:
{
BitmapHeapPath *bhpath;
FLAT_COPY_PATH(bhpath, path, BitmapHeapPath);
REPARAMETERIZE_CHILD_PATH(bhpath->bitmapqual);
new_path = (Path *) bhpath;
}
break;
case T_BitmapAndPath:
{
BitmapAndPath *bapath;
FLAT_COPY_PATH(bapath, path, BitmapAndPath);
REPARAMETERIZE_CHILD_PATH_LIST(bapath->bitmapquals);
new_path = (Path *) bapath;
}
break;
case T_BitmapOrPath:
{
BitmapOrPath *bopath;
FLAT_COPY_PATH(bopath, path, BitmapOrPath);
REPARAMETERIZE_CHILD_PATH_LIST(bopath->bitmapquals);
new_path = (Path *) bopath;
}
break;
case T_TidPath:
{
TidPath *tpath;
/*
* TidPath contains tidquals, which do not contain any
* external parameters per create_tidscan_path(). So don't
* bother to translate those.
*/
FLAT_COPY_PATH(tpath, path, TidPath);
new_path = (Path *) tpath;
}
break;
case T_ForeignPath:
{
ForeignPath *fpath;
ReparameterizeForeignPathByChild_function rfpc_func;
FLAT_COPY_PATH(fpath, path, ForeignPath);
if (fpath->fdw_outerpath)
REPARAMETERIZE_CHILD_PATH(fpath->fdw_outerpath);
/* Hand over to FDW if needed. */
rfpc_func =
path->parent->fdwroutine->ReparameterizeForeignPathByChild;
if (rfpc_func)
fpath->fdw_private = rfpc_func(root, fpath->fdw_private,
child_rel);
new_path = (Path *) fpath;
}
break;
case T_CustomPath:
{
CustomPath *cpath;
FLAT_COPY_PATH(cpath, path, CustomPath);
REPARAMETERIZE_CHILD_PATH_LIST(cpath->custom_paths);
if (cpath->methods &&
cpath->methods->ReparameterizeCustomPathByChild)
cpath->custom_private =
cpath->methods->ReparameterizeCustomPathByChild(root,
cpath->custom_private,
child_rel);
new_path = (Path *) cpath;
}
break;
case T_NestPath:
{
JoinPath *jpath;
FLAT_COPY_PATH(jpath, path, NestPath);
REPARAMETERIZE_CHILD_PATH(jpath->outerjoinpath);
REPARAMETERIZE_CHILD_PATH(jpath->innerjoinpath);
ADJUST_CHILD_ATTRS(jpath->joinrestrictinfo);
new_path = (Path *) jpath;
}
break;
case T_MergePath:
{
JoinPath *jpath;
MergePath *mpath;
FLAT_COPY_PATH(mpath, path, MergePath);
jpath = (JoinPath *) mpath;
REPARAMETERIZE_CHILD_PATH(jpath->outerjoinpath);
REPARAMETERIZE_CHILD_PATH(jpath->innerjoinpath);
ADJUST_CHILD_ATTRS(jpath->joinrestrictinfo);
ADJUST_CHILD_ATTRS(mpath->path_mergeclauses);
new_path = (Path *) mpath;
}
break;
case T_HashPath:
{
JoinPath *jpath;
HashPath *hpath;
FLAT_COPY_PATH(hpath, path, HashPath);
jpath = (JoinPath *) hpath;
REPARAMETERIZE_CHILD_PATH(jpath->outerjoinpath);
REPARAMETERIZE_CHILD_PATH(jpath->innerjoinpath);
ADJUST_CHILD_ATTRS(jpath->joinrestrictinfo);
ADJUST_CHILD_ATTRS(hpath->path_hashclauses);
new_path = (Path *) hpath;
}
break;
case T_AppendPath:
{
AppendPath *apath;
FLAT_COPY_PATH(apath, path, AppendPath);
REPARAMETERIZE_CHILD_PATH_LIST(apath->subpaths);
new_path = (Path *) apath;
}
break;
case T_MergeAppend:
{
MergeAppendPath *mapath;
FLAT_COPY_PATH(mapath, path, MergeAppendPath);
REPARAMETERIZE_CHILD_PATH_LIST(mapath->subpaths);
new_path = (Path *) mapath;
}
break;
case T_MaterialPath:
{
MaterialPath *mpath;
FLAT_COPY_PATH(mpath, path, MaterialPath);
REPARAMETERIZE_CHILD_PATH(mpath->subpath);
new_path = (Path *) mpath;
}
break;
case T_UniquePath:
{
UniquePath *upath;
FLAT_COPY_PATH(upath, path, UniquePath);
REPARAMETERIZE_CHILD_PATH(upath->subpath);
ADJUST_CHILD_ATTRS(upath->uniq_exprs);
new_path = (Path *) upath;
}
break;
case T_GatherPath:
{
GatherPath *gpath;
FLAT_COPY_PATH(gpath, path, GatherPath);
REPARAMETERIZE_CHILD_PATH(gpath->subpath);
new_path = (Path *) gpath;
}
break;
case T_GatherMergePath:
{
GatherMergePath *gmpath;
FLAT_COPY_PATH(gmpath, path, GatherMergePath);
REPARAMETERIZE_CHILD_PATH(gmpath->subpath);
new_path = (Path *) gmpath;
}
break;
default:
/* We don't know how to reparameterize this path. */
return NULL;
}
/*
* Adjust the parameterization information, which refers to the topmost
* parent. The topmost parent can be multiple levels away from the given
* child, hence use multi-level expression adjustment routines.
*/
old_ppi = new_path->param_info;
required_outer =
adjust_child_relids_multilevel(root, old_ppi->ppi_req_outer,
child_rel->relids,
child_rel->top_parent_relids);
/* If we already have a PPI for this parameterization, just return it */
new_ppi = find_param_path_info(new_path->parent, required_outer);
/*
* If not, build a new one and link it to the list of PPIs. For the same
* reason as explained in mark_dummy_rel(), allocate new PPI in the same
* context the given RelOptInfo is in.
*/
if (new_ppi == NULL)
{
MemoryContext oldcontext;
RelOptInfo *rel = path->parent;
oldcontext = MemoryContextSwitchTo(GetMemoryChunkContext(rel));
new_ppi = makeNode(ParamPathInfo);
new_ppi->ppi_req_outer = bms_copy(required_outer);
new_ppi->ppi_rows = old_ppi->ppi_rows;
new_ppi->ppi_clauses = old_ppi->ppi_clauses;
ADJUST_CHILD_ATTRS(new_ppi->ppi_clauses);
rel->ppilist = lappend(rel->ppilist, new_ppi);
MemoryContextSwitchTo(oldcontext);
}
bms_free(required_outer);
new_path->param_info = new_ppi;
/*
* Adjust the path target if the parent of the outer relation is
* referenced in the targetlist. This can happen when only the parent of
* outer relation is laterally referenced in this relation.
*/
if (bms_overlap(path->parent->lateral_relids,
child_rel->top_parent_relids))
{
new_path->pathtarget = copy_pathtarget(new_path->pathtarget);
ADJUST_CHILD_ATTRS(new_path->pathtarget->exprs);
}
return new_path;
}
/*
* reparameterize_pathlist_by_child
* Helper function to reparameterize a list of paths by given child rel.
*/
static List *
reparameterize_pathlist_by_child(PlannerInfo *root,
List *pathlist,
RelOptInfo *child_rel)
{
ListCell *lc;
List *result = NIL;
foreach(lc, pathlist)
{
Path *path = reparameterize_path_by_child(root, lfirst(lc),
child_rel);
if (path == NULL)
{
list_free(result);
return NIL;
}
result = lappend(result, path);
}
return result;
}