From 5278d0a2e870c61f9374a7796b90e6f9f6a73638 Mon Sep 17 00:00:00 2001 From: Amit Langote Date: Mon, 25 Mar 2024 12:02:40 +0900 Subject: [PATCH] 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 Reviewed-by: Richard Guo Reviewed-by: Amit Langote Reviewed-by: Andrey Lepikhov Reviewed-by: Tomas Vondra Discussion: https://postgr.es/m/CAExHW5tHqEf3ASVqvFFcghYGPfpy7o3xnvhHwBGbJFMRH8KjNw@mail.gmail.com --- src/backend/optimizer/path/joinrels.c | 36 +++++++++++++++++++++++++++ src/backend/optimizer/util/pathnode.c | 5 ++-- src/include/nodes/pathnodes.h | 3 +++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c index 4750579b0a7..c59aff28226 100644 --- a/src/backend/optimizer/path/joinrels.c +++ b/src/backend/optimizer/path/joinrels.c @@ -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 diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 0a7e5c2678f..c29ca5a0da2 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -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); diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index b3069516b24..08dbee002fe 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -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; -- 2.47.2