Reduce memory used by partitionwise joins

Specifically, this commit reduces the memory consumed by the
SpecialJoinInfos that are allocated for child joins in
try_partitionwise_join() by freeing them at the end of creating paths
for each child join.

A SpecialJoinInfo allocated for a given child join is a copy of the
parent join's SpecialJoinInfo, which contains the translated copies
of the various Relids bitmapsets and semi_rhs_exprs, which is a List
of Nodes.  The newly added freeing step frees the struct itself and
the various bitmapsets, but not semi_rhs_exprs, because there's no
handy function to free the memory of Node trees.

Author: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
Reviewed-by: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: Amit Langote <amitlangote09@gmail.com>
Reviewed-by: Andrey Lepikhov <a.lepikhov@postgrespro.ru>
Reviewed-by: Tomas Vondra <tomas.vondra@enterprisedb.com>
Discussion: https://postgr.es/m/CAExHW5tHqEf3ASVqvFFcghYGPfpy7o3xnvhHwBGbJFMRH8KjNw@mail.gmail.com
This commit is contained in:
Amit Langote 2024-03-25 12:02:40 +09:00
parent 619bc23a1a
commit 5278d0a2e8
3 changed files with 42 additions and 2 deletions

View File

@ -45,6 +45,7 @@ static void try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1,
static SpecialJoinInfo *build_child_join_sjinfo(PlannerInfo *root,
SpecialJoinInfo *parent_sjinfo,
Relids left_relids, Relids right_relids);
static void free_child_join_sjinfo(SpecialJoinInfo *child_sjinfo);
static void compute_partition_bounds(PlannerInfo *root, RelOptInfo *rel1,
RelOptInfo *rel2, RelOptInfo *joinrel,
SpecialJoinInfo *parent_sjinfo,
@ -1659,6 +1660,7 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
child_restrictlist);
pfree(appinfos);
free_child_join_sjinfo(child_sjinfo);
}
}
@ -1666,6 +1668,9 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
* Construct the SpecialJoinInfo for a child-join by translating
* SpecialJoinInfo for the join between parents. left_relids and right_relids
* are the relids of left and right side of the join respectively.
*
* If translations are added to or removed from this function, consider
* updating free_child_join_sjinfo() accordingly.
*/
static SpecialJoinInfo *
build_child_join_sjinfo(PlannerInfo *root, SpecialJoinInfo *parent_sjinfo,
@ -1705,6 +1710,37 @@ build_child_join_sjinfo(PlannerInfo *root, SpecialJoinInfo *parent_sjinfo,
return sjinfo;
}
/*
* free_child_join_sjinfo
* Free memory consumed by a SpecialJoinInfo created by
* build_child_join_sjinfo()
*
* Only members that are translated copies of their counterpart in the parent
* SpecialJoinInfo are freed here.
*/
static void
free_child_join_sjinfo(SpecialJoinInfo *sjinfo)
{
/*
* Dummy SpecialJoinInfos of inner joins do not have any translated fields
* and hence no fields that to be freed.
*/
if (sjinfo->jointype != JOIN_INNER)
{
bms_free(sjinfo->min_lefthand);
bms_free(sjinfo->min_righthand);
bms_free(sjinfo->syn_lefthand);
bms_free(sjinfo->syn_righthand);
/*
* semi_rhs_exprs may in principle be freed, but a simple pfree() does
* not suffice, so we leave it alone.
*/
}
pfree(sjinfo);
}
/*
* compute_partition_bounds
* Compute the partition bounds for a join rel from those for inputs

View File

@ -1707,8 +1707,9 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
pathnode->subpath = subpath;
/*
* Under GEQO, the sjinfo might be short-lived, so we'd better make copies
* of data structures we extract from it.
* Under GEQO and when planning child joins, the sjinfo might be
* short-lived, so we'd better make copies of data structures we extract
* from it.
*/
pathnode->in_operators = copyObject(sjinfo->semi_operators);
pathnode->uniq_exprs = copyObject(sjinfo->semi_rhs_exprs);

View File

@ -2856,6 +2856,9 @@ typedef struct PlaceHolderVar
* cost estimation purposes it is sometimes useful to know the join size under
* plain innerjoin semantics. Note that lhs_strict and the semi_xxx fields
* are not set meaningfully within such structs.
*
* We also create transient SpecialJoinInfos for child joins during
* partiotionwise join planning, which are also not present in join_info_list.
*/
#ifndef HAVE_SPECIALJOININFO_TYPEDEF
typedef struct SpecialJoinInfo SpecialJoinInfo;