v1-0002-Some-fixes.patch

application/x-patch

Filename: v1-0002-Some-fixes.patch
Type: application/x-patch
Part: 0
Message: Re: Memory consumed by child SpecialJoinInfo in partitionwise join planning

Patch

Format: format-patch
Series: patch v1-0002
Subject: Some fixes
File+
src/backend/optimizer/path/costsize.c 3 3
src/backend/optimizer/path/joinrels.c 62 63
src/backend/optimizer/util/pathnode.c 3 2
src/include/optimizer/paths.h 2 2
From 9d8c408b3379faab6269421f67d75530f1c4cde2 Mon Sep 17 00:00:00 2001
From: Amit Langote <amitlan@postgresql.org>
Date: Fri, 22 Mar 2024 16:51:48 +0900
Subject: [PATCH v1 2/2] Some fixes

* Revert (IMO) unnecessary modifications of build_child_join_sjinfo().
For example, hunks to rename sjinfo to child_sjinfo seem unnecessary,
because it's clear from the context that they are "child" sjinfos.

* Rename free_child_sjinfo_members() to free_child_join_sjinfo() to
be symmetric with build_child_join_sjinfo().  Note that the function
is charged with freeing the sjinfo itself too.  Like
build_child_join_sjinfo(), name the argument sjinfo, not
child_sjinfo.

* Don't set the child sjinfo pointer to NULL after freeing it.  While
a good convention in general, it's not really common in the planner
code, so doing it here seems a bit overkill

* Rename make_dummy_sjinfo() to init_dummy_sjinfo() because the
function is not really making it per se, only initializing fields
in the SpecialJoinInfo struct made available by the caller.

* Make init_dummy_sjinfo() take Relids instead of RelOptInfos because
that's what's needed.  Also allows to reduce changes to
build_child_join_sjinfo()'s signature.

* Update the reason mentioned in a comment in free_child_join_sjinfo()
about why semi_rhs_expr is not freed -- it may be freed, but there's
no way to "deep" free it, so we leave it alone.

* Update a comment somewhere about why SpecialJoinInfo can be
"transient" sometimes.
---
 src/backend/optimizer/path/costsize.c |   6 +-
 src/backend/optimizer/path/joinrels.c | 125 +++++++++++++-------------
 src/backend/optimizer/util/pathnode.c |   5 +-
 src/include/optimizer/paths.h         |   4 +-
 4 files changed, 70 insertions(+), 70 deletions(-)

diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 1b76523e79..ee23ed7835 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5050,7 +5050,7 @@ compute_semi_anti_join_factors(PlannerInfo *root,
 	/*
 	 * Also get the normal inner-join selectivity of the join clauses.
 	 */
-	make_dummy_sjinfo(&norm_sjinfo, outerrel, innerrel);
+	init_dummy_sjinfo(&norm_sjinfo, outerrel->relids, innerrel->relids);
 
 	nselec = clauselist_selectivity(root,
 									joinquals,
@@ -5203,8 +5203,8 @@ approx_tuple_count(PlannerInfo *root, JoinPath *path, List *quals)
 	/*
 	 * Make up a SpecialJoinInfo for JOIN_INNER semantics.
 	 */
-	make_dummy_sjinfo(&sjinfo, path->outerjoinpath->parent,
-					  path->innerjoinpath->parent);
+	init_dummy_sjinfo(&sjinfo, path->outerjoinpath->parent->relids,
+					  path->innerjoinpath->parent->relids);
 
 	/* Get the approximate selectivity */
 	foreach(l, quals)
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 465dcf7d5d..bef5b5f213 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -44,9 +44,8 @@ static void try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1,
 								   List *parent_restrictlist);
 static SpecialJoinInfo *build_child_join_sjinfo(PlannerInfo *root,
 												SpecialJoinInfo *parent_sjinfo,
-												RelOptInfo *left_child,
-												RelOptInfo *right_child);
-static void free_child_sjinfo_members(SpecialJoinInfo **child_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,
@@ -656,18 +655,24 @@ join_is_legal(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 }
 
 /*
- * make_dummy_sjinfo
- *    Populate the given SpecialJoinInfo for a plain inner join, between rel1
- *    and rel2, which does not have a SpecialJoinInfo associated with it.
+ * init_dummy_sjinfo
+ *    Populate the given SpecialJoinInfo for a plain inner join between rel1
+ *    and rel2
+ *
+ * Inner joins do not normally have an associated SpecialJoinInfo node.  But
+ * some functions involved in join planning require one to be passed,
+ * containing at least the information of which relations are being joined,
+ * so we initialize that information here.
  */
 void
-make_dummy_sjinfo(SpecialJoinInfo *sjinfo, RelOptInfo *rel1, RelOptInfo *rel2)
+init_dummy_sjinfo(SpecialJoinInfo *sjinfo, Relids left_relids,
+				  Relids right_relids)
 {
 	sjinfo->type = T_SpecialJoinInfo;
-	sjinfo->min_lefthand = rel1->relids;
-	sjinfo->min_righthand = rel2->relids;
-	sjinfo->syn_lefthand = rel1->relids;
-	sjinfo->syn_righthand = rel2->relids;
+	sjinfo->min_lefthand = left_relids;
+	sjinfo->min_righthand = right_relids;
+	sjinfo->syn_lefthand = left_relids;
+	sjinfo->syn_righthand = right_relids;
 	sjinfo->jointype = JOIN_INNER;
 	sjinfo->ojrelid = 0;
 	sjinfo->commute_above_l = NULL;
@@ -744,7 +749,7 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
 	if (sjinfo == NULL)
 	{
 		sjinfo = &sjinfo_data;
-		make_dummy_sjinfo(sjinfo, rel1, rel2);
+		init_dummy_sjinfo(sjinfo, rel1->relids, rel2->relids);
 	}
 
 	/*
@@ -1628,8 +1633,9 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 		 * Construct SpecialJoinInfo from parent join relations's
 		 * SpecialJoinInfo.
 		 */
-		child_sjinfo = build_child_join_sjinfo(root, parent_sjinfo, child_rel1,
-											   child_rel2);
+		child_sjinfo = build_child_join_sjinfo(root, parent_sjinfo,
+											   child_rel1->relids,
+											   child_rel2->relids);
 
 		/* Find the AppendRelInfo structures */
 		appinfos = find_appinfos_by_relids(root,
@@ -1670,7 +1676,7 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 									child_restrictlist);
 
 		pfree(appinfos);
-		free_child_sjinfo_members(&child_sjinfo);
+		free_child_join_sjinfo(child_sjinfo);
 	}
 }
 
@@ -1679,91 +1685,84 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
  * 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 freeing
- * the translated objects in free_child_sjinfo_members() appropriately.
+ * If translations are added to or removed from this function, consider
+ * updating free_child_join_sjinfo() appropriately.
  */
 static SpecialJoinInfo *
 build_child_join_sjinfo(PlannerInfo *root, SpecialJoinInfo *parent_sjinfo,
-						RelOptInfo *left_child, RelOptInfo *right_child)
+						Relids left_relids, Relids right_relids)
 {
+	SpecialJoinInfo *sjinfo = makeNode(SpecialJoinInfo);
 	AppendRelInfo **left_appinfos;
 	int			left_nappinfos;
 	AppendRelInfo **right_appinfos;
 	int			right_nappinfos;
-	SpecialJoinInfo *child_sjinfo = makeNode(SpecialJoinInfo);
 
 	/* Dummy SpecialJoinInfos can be created without any translation. */
 	if (parent_sjinfo->jointype == JOIN_INNER)
 	{
 		Assert(parent_sjinfo->ojrelid == 0);
-		make_dummy_sjinfo(child_sjinfo, left_child, right_child);
-		return child_sjinfo;
+		init_dummy_sjinfo(sjinfo, left_relids, right_relids);
+		return sjinfo;
 	}
 
-	memcpy(child_sjinfo, parent_sjinfo, sizeof(SpecialJoinInfo));
-	left_appinfos = find_appinfos_by_relids(root, left_child->relids,
+	memcpy(sjinfo, parent_sjinfo, sizeof(SpecialJoinInfo));
+	left_appinfos = find_appinfos_by_relids(root, left_relids,
 											&left_nappinfos);
-	right_appinfos = find_appinfos_by_relids(root, right_child->relids,
+	right_appinfos = find_appinfos_by_relids(root, right_relids,
 											 &right_nappinfos);
 
-	child_sjinfo->min_lefthand =
-		adjust_child_relids(child_sjinfo->min_lefthand,
-							left_nappinfos,
-							left_appinfos);
-	child_sjinfo->min_righthand =
-		adjust_child_relids(child_sjinfo->min_righthand,
-							right_nappinfos,
-							right_appinfos);
-	child_sjinfo->syn_lefthand = adjust_child_relids(child_sjinfo->syn_lefthand,
-													 left_nappinfos,
-													 left_appinfos);
-	child_sjinfo->syn_righthand = adjust_child_relids(child_sjinfo->syn_righthand,
-													  right_nappinfos,
-													  right_appinfos);
-
+	sjinfo->min_lefthand = adjust_child_relids(sjinfo->min_lefthand,
+											   left_nappinfos, left_appinfos);
+	sjinfo->min_righthand = adjust_child_relids(sjinfo->min_righthand,
+												right_nappinfos,
+												right_appinfos);
+	sjinfo->syn_lefthand = adjust_child_relids(sjinfo->syn_lefthand,
+											   left_nappinfos, left_appinfos);
+	sjinfo->syn_righthand = adjust_child_relids(sjinfo->syn_righthand,
+												right_nappinfos,
+												right_appinfos);
 	/* outer-join relids need no adjustment */
-	child_sjinfo->semi_rhs_exprs =
-		(List *) adjust_appendrel_attrs(root,
-										(Node *) child_sjinfo->semi_rhs_exprs,
-										right_nappinfos, right_appinfos);
+	sjinfo->semi_rhs_exprs = (List *) adjust_appendrel_attrs(root,
+															 (Node *) sjinfo->semi_rhs_exprs,
+															 right_nappinfos,
+															 right_appinfos);
 
 	pfree(left_appinfos);
 	pfree(right_appinfos);
 
-	return child_sjinfo;
+	return sjinfo;
 }
 
 /*
- * free_child_sjinfo_members
- *		Free memory consumed by a child SpecialJoinInfo.
+ * 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.  However, members that could be referenced
- * elsewhere are not freed.
+ * SpecialJoinInfo are freed here; see build_child_join_sjinfo().
  */
 static void
-free_child_sjinfo_members(SpecialJoinInfo **child_sjinfo_p)
+free_child_join_sjinfo(SpecialJoinInfo *sjinfo)
 {
-	SpecialJoinInfo *child_sjinfo = *child_sjinfo_p;
-
 	/*
-	 * Dummy SpecialJoinInfos do not have any translated fields and hence have
-	 * nothing to free.
+	 * Dummy SpecialJoinInfos of inner joins do not have any translated fields
+	 * and hence have nothing to be freed; see init_dummy_sjinfo().
 	 */
-	if (child_sjinfo->jointype != JOIN_INNER)
+	if (sjinfo->jointype != JOIN_INNER)
 	{
-		bms_free(child_sjinfo->min_lefthand);
-		bms_free(child_sjinfo->min_righthand);
-		bms_free(child_sjinfo->syn_lefthand);
-		bms_free(child_sjinfo->syn_righthand);
+		bms_free(sjinfo->min_lefthand);
+		bms_free(sjinfo->min_righthand);
+		bms_free(sjinfo->syn_lefthand);
+		bms_free(sjinfo->syn_righthand);
 
-		/* semi_rhs_exprs may be referenced, so don't free. */
+		/*
+		 * semi_rhs_exprs may in principle be freed, but a simple pfree() does
+		 * not suffice, so we leave it alone.
+		 */
 	}
 
-	/* Avoid dangling pointer. */
-	*child_sjinfo_p = NULL;
-
-	pfree(child_sjinfo);
+	pfree(sjinfo);
 }
 
 /*
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 0a7e5c2678..c29ca5a0da 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/optimizer/paths.h b/src/include/optimizer/paths.h
index 1e340a6335..125676b5bf 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -112,8 +112,8 @@ extern bool have_join_order_restriction(PlannerInfo *root,
 extern bool have_dangerous_phv(PlannerInfo *root,
 							   Relids outer_relids, Relids inner_params);
 extern void mark_dummy_rel(RelOptInfo *rel);
-extern void make_dummy_sjinfo(SpecialJoinInfo *sjinfo, RelOptInfo *rel1,
-							  RelOptInfo *rel2);
+extern void init_dummy_sjinfo(SpecialJoinInfo *sjinfo, Relids left_relids,
+							  Relids right_relids);
 
 /*
  * equivclass.c
-- 
2.43.0