0001-RestrictInfo-hash-table-interface-20250220.patch

text/x-patch

Filename: 0001-RestrictInfo-hash-table-interface-20250220.patch
Type: text/x-patch
Part: 0
Message: Re: 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 0001
Subject: RestrictInfo hash table interface
File+
src/backend/optimizer/util/restrictinfo.c 142 1
src/include/nodes/pathnodes.h 3 0
src/include/optimizer/restrictinfo.h 3 0
src/tools/pgindent/typedefs.list 2 0
From 8704d0ca210e763547d4eea61b00d5a13cac8a9e Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
Date: Thu, 26 Sep 2024 12:06:25 +0530
Subject: [PATCH 1/6] RestrictInfo hash table interface

Introduces the RestrictInfo hash table interface and adds a pointer to
RestrictInfo hash table in PlannerInfo. RestrictInfo::rinfo_serial and
RestrictInfo::required_relids are used as key into the hash table.

This commit does not add any code which uses the interface itself.
Adding a new member to PlannerInfo increases it size and may have slight
impact due to cacheline faults when accessing this huge structure.
Similarly the interface code increases object file size which again may
have some impact on performance. This is a separate commit to assess any
performance or memory impact this change has.

Ashutosh Bapat
---
 src/backend/optimizer/util/restrictinfo.c | 143 +++++++++++++++++++++-
 src/include/nodes/pathnodes.h             |   3 +
 src/include/optimizer/restrictinfo.h      |   3 +
 src/tools/pgindent/typedefs.list          |   2 +
 4 files changed, 150 insertions(+), 1 deletion(-)

diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index a80083d2323..9279062a877 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -19,7 +19,7 @@
 #include "optimizer/clauses.h"
 #include "optimizer/optimizer.h"
 #include "optimizer/restrictinfo.h"
-
+#include "common/hashfn.h"
 
 static Expr *make_sub_restrictinfos(PlannerInfo *root,
 									Expr *clause,
@@ -676,3 +676,144 @@ join_clause_is_movable_into(RestrictInfo *rinfo,
 
 	return true;
 }
+
+/* ----------------------------------------------------------------------------
+ * RestrictInfo hash table interface.
+ *
+ * For storing and retrieving child RestrictInfos. Though the interface is used
+ * for child RestrictInfos, it can be used for parent RestrictInfos as well.
+ * Hence the names of functions and structures do not mention child in it unless
+ * necessary.
+ *
+ * RestrictInfo::rinfo_serial is unique for a set of RestrictInfos, all of
+ * which, are versions of same condition.  RestrictInfo::required_relids
+ * identifies the relations to which the RestrictInfo is applicable.  Together
+ * they are used as a key into the hash table since they uniquely identify a
+ * RestictInfo.
+ * ----------------------------------------------------------------------------
+ */
+
+/*
+ * Hash key for RestrictInfo hash table.
+ */
+typedef struct
+{
+	int			rinfo_serial;	/* Same as RestrictInfo::rinfo_serial */
+	Relids		required_relids;	/* Same as RestrictInfo::required_relids */
+} rinfo_tab_key;
+
+/* Hash table entry for RestrictInfo hash table. */
+typedef struct rinfo_tab_entry
+{
+	rinfo_tab_key key;			/* Key must be first. */
+	RestrictInfo *rinfo;
+} rinfo_tab_entry;
+
+/*
+ * rinfo_tab_key_hash
+ *		Computes hash of RestrictInfo hash table key.
+ */
+static uint32
+rinfo_tab_key_hash(const void *key, Size size)
+{
+	rinfo_tab_key *rtabkey = (rinfo_tab_key *) key;
+	uint32		result;
+
+	Assert(sizeof(rinfo_tab_key) == size);
+
+	/* Combine hashes of all components of the key. */
+	result = hash_bytes_uint32(rtabkey->rinfo_serial);
+	result = hash_combine(result, bms_hash_value(rtabkey->required_relids));
+
+	return result;
+}
+
+/*
+ * rinfo_tab_key_match
+ *		Match function for RestrictInfo hash table.
+ */
+static int
+rinfo_tab_key_match(const void *key1, const void *key2, Size size)
+{
+	rinfo_tab_key *rtabkey1 = (rinfo_tab_key *) key1;
+	rinfo_tab_key *rtabkey2 = (rinfo_tab_key *) key2;
+	int			result;
+
+	Assert(sizeof(rinfo_tab_key) == size);
+
+	result = rtabkey1->rinfo_serial - rtabkey2->rinfo_serial;
+	if (result)
+		return result;
+
+	return !bms_equal(rtabkey1->required_relids, rtabkey2->required_relids);
+}
+
+/*
+ * get_child_rinfo_hash
+ *		Returns the child RestrictInfo hash table from PlannerInfo, creating it if
+ *		necessary.
+ */
+static HTAB *
+get_child_rinfo_hash(PlannerInfo *root)
+{
+	if (!root->child_rinfo_hash)
+	{
+		HASHCTL		hash_ctl = {0};
+
+		hash_ctl.keysize = sizeof(rinfo_tab_key);
+		hash_ctl.entrysize = sizeof(rinfo_tab_entry);
+		hash_ctl.hcxt = root->planner_cxt;
+		hash_ctl.hash = rinfo_tab_key_hash;
+		hash_ctl.match = rinfo_tab_key_match;
+
+		root->child_rinfo_hash = hash_create("restrictinfo hash table",
+											 1000,
+											 &hash_ctl,
+											 HASH_ELEM | HASH_CONTEXT | HASH_FUNCTION | HASH_COMPARE);
+	}
+
+	return root->child_rinfo_hash;
+}
+
+/*
+ * add_restrictinfo
+ *		Add the given RestrictInfo to the RestrictInfo hash table.
+ */
+extern void
+add_restrictinfo(PlannerInfo *root, RestrictInfo *rinfo)
+{
+	HTAB	   *rinfo_hash = get_child_rinfo_hash(root);
+	rinfo_tab_key key;
+	rinfo_tab_entry *rinfo_entry;
+	bool		found;
+
+	key.rinfo_serial = rinfo->rinfo_serial;
+	key.required_relids = rinfo->required_relids;
+	rinfo_entry = hash_search(rinfo_hash, &key, HASH_ENTER, &found);
+
+	Assert(!found);
+	rinfo_entry->rinfo = rinfo;
+}
+
+/*
+ * find_restrictinfo
+ *		Return the RestrictInfo with given rinfo_serial and
+ *		required_relids from RestrictInfo hash table.
+ *
+ * The function returns NULL if it does not find the required RestrictInfo in
+ * the hash table.
+ */
+RestrictInfo *
+find_restrictinfo(PlannerInfo *root, int rinfo_serial, Relids required_relids)
+{
+	HTAB	   *rinfo_hash = get_child_rinfo_hash(root);
+	rinfo_tab_entry *rinfo_entry;
+	rinfo_tab_key key;
+
+	key.rinfo_serial = rinfo_serial;
+	key.required_relids = required_relids;
+	rinfo_entry = hash_search(rinfo_hash, &key,
+							  HASH_FIND,
+							  NULL);
+	return (rinfo_entry ? rinfo_entry->rinfo : NULL);
+}
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index fbf05322c75..83dc2ea3781 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -363,6 +363,9 @@ struct PlannerInfo
 	/* counter for assigning RestrictInfo serial numbers */
 	int			last_rinfo_serial;
 
+	/* Hash table to store and retrieve child RestrictInfos. */
+	struct HTAB *child_rinfo_hash pg_node_attr(read_write_ignore);
+
 	/*
 	 * all_result_relids is empty for SELECT, otherwise it contains at least
 	 * parse->resultRelation.  For UPDATE/DELETE/MERGE across an inheritance
diff --git a/src/include/optimizer/restrictinfo.h b/src/include/optimizer/restrictinfo.h
index ec91fc9c583..57d2210ce95 100644
--- a/src/include/optimizer/restrictinfo.h
+++ b/src/include/optimizer/restrictinfo.h
@@ -58,6 +58,9 @@ extern bool join_clause_is_movable_to(RestrictInfo *rinfo, RelOptInfo *baserel);
 extern bool join_clause_is_movable_into(RestrictInfo *rinfo,
 										Relids currentrelids,
 										Relids current_and_outer);
+extern RestrictInfo *find_restrictinfo(PlannerInfo *root, int rinfo_serial,
+									   Relids child_required_relids);
+extern void add_restrictinfo(PlannerInfo *root, RestrictInfo *child_rinfo);
 
 /*
  * clause_sides_match_join
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 98ab45adfa3..2a1acb50a95 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3915,6 +3915,8 @@ rewind_source
 rewrite_event
 rf_context
 rfile
+rinfo_tab_entry
+rinfo_tab_key
 rm_detail_t
 role_auth_extra
 rolename_hash

base-commit: 1fd1bd871012732e3c6c482667d2f2c56f1a9395
-- 
2.34.1