0002-Avoid-translating-RestrictInfo-repeatedly-20230718.patch

application/x-patch

Filename: 0002-Avoid-translating-RestrictInfo-repeatedly-20230718.patch
Type: application/x-patch
Part: 3
Message: Reducing memory consumed by RestrictInfo list translations in partitionwise join planning

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: format-patch
Series: patch 0002
Subject: Avoid translating RestrictInfo repeatedly
File+
src/backend/optimizer/util/appendinfo.c 46 1
src/backend/optimizer/util/restrictinfo.c 2 0
src/include/nodes/pathnodes.h 7 0
From 99f06a021fc4330ba1fcbf61f4c0e085b96df500 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <ashutosh.bapat@enterprisedb.com>
Date: Thu, 22 Jun 2023 14:27:14 +0530
Subject: [PATCH 2/7] Avoid translating RestrictInfo repeatedly

RestrictInfo to be applied to the child rels (including the child join rels)
are obtained by translating RestrictInfo applicable to the parent rel. Since
these translations are not tracked, the same RestrictInfo may get translated
multiple times for the same parent and child pair. When using partitionwise
join this can happen as many times as the number of possible join orders
between the partitioned tables.

Every translated RestrictInfo is stored in the top parent's RestrictInfo in a
list. Every translated RestrictInfo contains a pointer to the top parent's
RestrictInfo to track multilevel translations. RestrictInfo::required_relids is
used as a key to search a given translation. adjust_appendrel_attrs_mutator()
first looks up an existing translations when translating a RestrictInfo and
creates a new one only when one doesn't exist.

Ashutosh Bapat
---
 src/backend/optimizer/util/appendinfo.c   | 47 ++++++++++++++++++++++-
 src/backend/optimizer/util/restrictinfo.c |  2 +
 src/include/nodes/pathnodes.h             |  7 ++++
 3 files changed, 55 insertions(+), 1 deletion(-)

diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index f456b3b0a4..70a5ff2750 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -217,6 +217,7 @@ adjust_appendrel_attrs_mutator(Node *node,
 {
 	AppendRelInfo **appinfos = context->appinfos;
 	int			nappinfos = context->nappinfos;
+	PlannerInfo	   *root = context->root;
 	int			cnt;
 
 	if (node == NULL)
@@ -445,7 +446,46 @@ adjust_appendrel_attrs_mutator(Node *node,
 	if (IsA(node, RestrictInfo))
 	{
 		RestrictInfo *oldinfo = (RestrictInfo *) node;
-		RestrictInfo *newinfo = makeNode(RestrictInfo);
+		Relids child_required_relids = adjust_child_relids(oldinfo->required_relids,
+															nappinfos, appinfos);
+		RestrictInfo   *parent_rinfo;
+		ListCell   *lc;
+		RestrictInfo *newinfo;
+		MemoryContext	old_context;
+		
+		/*
+		 * If the adjusted RestrictInfo already exists, use it otherwise create
+		 * a new one. This avoids translating the same RestrictInfo again and
+		 * again wasting memory especially in partitionwise joins.
+		 */
+
+		if (bms_equal(child_required_relids, oldinfo->required_relids))
+		{
+			/* If the clause does not need any translation. */
+			bms_free(child_required_relids);
+			return (Node *) oldinfo;
+		}
+
+		/*
+		 * Check if we already have the RestrictInfo for the given child in the
+		 * topmost parent's RestrictInfo.
+		 */
+		parent_rinfo = oldinfo->parent_rinfo ? oldinfo->parent_rinfo : oldinfo;
+		foreach (lc, parent_rinfo->child_rinfos)
+		{
+			newinfo = lfirst(lc);
+
+			if (bms_equal(newinfo->required_relids, child_required_relids))
+			{
+				bms_free(child_required_relids);
+				return (Node *) newinfo;
+			}
+		}
+		bms_free(child_required_relids);
+
+		/* Translate in a long lasting memory context. */
+		old_context = MemoryContextSwitchTo(root->planner_cxt);
+		newinfo = makeNode(RestrictInfo);
 
 		/* Copy all flat-copiable fields, notably including rinfo_serial */
 		memcpy(newinfo, oldinfo, sizeof(RestrictInfo));
@@ -491,6 +531,11 @@ adjust_appendrel_attrs_mutator(Node *node,
 		newinfo->right_bucketsize = -1;
 		newinfo->left_mcvfreq = -1;
 		newinfo->right_mcvfreq = -1;
+		newinfo->parent_rinfo = parent_rinfo;
+		parent_rinfo->child_rinfos = lappend(parent_rinfo->child_rinfos,
+											 newinfo);
+
+		MemoryContextSwitchTo(old_context);
 
 		return (Node *) newinfo;
 	}
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index d6d26a2b51..7a97ab7407 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -246,6 +246,8 @@ make_restrictinfo_internal(PlannerInfo *root,
 
 	restrictinfo->left_hasheqoperator = InvalidOid;
 	restrictinfo->right_hasheqoperator = InvalidOid;
+	restrictinfo->child_rinfos = NIL;
+	restrictinfo->parent_rinfo = NULL;
 
 	return restrictinfo;
 }
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index c17b53f7ad..a064629626 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -2655,6 +2655,13 @@ typedef struct RestrictInfo
 	/* hash equality operators used for memoize nodes, else InvalidOid */
 	Oid			left_hasheqoperator pg_node_attr(equal_ignore);
 	Oid			right_hasheqoperator pg_node_attr(equal_ignore);
+
+	/* Only one of these two can be set. */
+	List	   *child_rinfos pg_node_attr(equal_ignore, copy_as(NIL)); /* RestrictInfos derived for children. */
+	struct RestrictInfo *parent_rinfo pg_node_attr(equal_ignore, copy_as(NULL));		/* Parent restrictinfo this
+											 * RestrictInf is derived from.
+											 */
+
 } RestrictInfo;
 
 /*
-- 
2.25.1