From 6eeeecb060d3d75644946dc9404a3a65f8928813 Mon Sep 17 00:00:00 2001
From: Alexander Korotkov <akorotkov@postgresql.org>
Date: Wed, 7 Jun 2023 13:04:58 +0300
Subject: [PATCH 03/13] Allow table AM to store complex data structures in
 rd_amcache

New table AM method free_rd_amcache is responsible for freeing the rd_amcache.
---
 src/backend/access/heap/heapam_handler.c |  1 +
 src/backend/utils/cache/relcache.c       | 18 +++++++------
 src/include/access/tableam.h             | 33 ++++++++++++++++++++++++
 src/include/utils/rel.h                  | 10 ++++---
 4 files changed, 50 insertions(+), 12 deletions(-)

diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index 7c7204a2422..da86ca5c31a 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -2640,6 +2640,7 @@ static const TableAmRoutine heapam_methods = {
 	.index_build_range_scan = heapam_index_build_range_scan,
 	.index_validate_scan = heapam_index_validate_scan,
 
+	.free_rd_amcache = NULL,
 	.relation_size = table_block_relation_size,
 	.relation_needs_toast_table = heapam_relation_needs_toast_table,
 	.relation_toast_am = heapam_relation_toast_am,
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 2cd19d603fb..6d98bdfba06 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -318,6 +318,7 @@ static OpClassCacheEnt *LookupOpclassInfo(Oid operatorClassOid,
 										  StrategyNumber numSupport);
 static void RelationCacheInitFileRemoveInDir(const char *tblspcpath);
 static void unlink_initfile(const char *initfilename, int elevel);
+static void release_rd_amcache(Relation rel);
 
 
 /*
@@ -2262,9 +2263,7 @@ RelationReloadIndexInfo(Relation relation)
 	RelationCloseSmgr(relation);
 
 	/* Must free any AM cached data upon relcache flush */
-	if (relation->rd_amcache)
-		pfree(relation->rd_amcache);
-	relation->rd_amcache = NULL;
+	release_rd_amcache(relation);
 
 	/*
 	 * If it's a shared index, we might be called before backend startup has
@@ -2484,8 +2483,7 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
 		pfree(relation->rd_options);
 	if (relation->rd_indextuple)
 		pfree(relation->rd_indextuple);
-	if (relation->rd_amcache)
-		pfree(relation->rd_amcache);
+	release_rd_amcache(relation);
 	if (relation->rd_fdwroutine)
 		pfree(relation->rd_fdwroutine);
 	if (relation->rd_indexcxt)
@@ -2547,9 +2545,7 @@ RelationClearRelation(Relation relation, bool rebuild)
 	RelationCloseSmgr(relation);
 
 	/* Free AM cached data, if any */
-	if (relation->rd_amcache)
-		pfree(relation->rd_amcache);
-	relation->rd_amcache = NULL;
+	release_rd_amcache(relation);
 
 	/*
 	 * Treat nailed-in system relations separately, they always need to be
@@ -6868,3 +6864,9 @@ ResOwnerReleaseRelation(Datum res)
 
 	RelationCloseCleanup((Relation) res);
 }
+
+static void
+release_rd_amcache(Relation rel)
+{
+	table_free_rd_amcache(rel);
+}
diff --git a/src/include/access/tableam.h b/src/include/access/tableam.h
index 467bdc09d36..be092e8bedb 100644
--- a/src/include/access/tableam.h
+++ b/src/include/access/tableam.h
@@ -715,6 +715,13 @@ typedef struct TableAmRoutine
 	 * ------------------------------------------------------------------------
 	 */
 
+	/*
+	 * This callback frees relation private cache data stored in rd_amcache.
+	 * If this callback is not provided, rd_amcache is assumed to point to
+	 * single memory chunk.
+	 */
+	void		(*free_rd_amcache) (Relation rel);
+
 	/*
 	 * See table_relation_size().
 	 *
@@ -1878,6 +1885,32 @@ table_index_validate_scan(Relation table_rel,
  * ----------------------------------------------------------------------------
  */
 
+/*
+ * Frees relation private cache data stored in rd_amcache.  Uses
+ * free_rd_amcache method if provided.  Assumes rd_amcache to point to single
+ * memory chunk otherwise.
+ */
+static inline void
+table_free_rd_amcache(Relation rel)
+{
+	if (rel->rd_tableam && rel->rd_tableam->free_rd_amcache)
+	{
+		rel->rd_tableam->free_rd_amcache(rel);
+
+		/*
+		 * We are assuming free_rd_amcache() did clear the cache and left NULL
+		 * in rd_amcache.
+		 */
+		Assert(rel->rd_amcache == NULL);
+	}
+	else
+	{
+		if (rel->rd_amcache)
+			pfree(rel->rd_amcache);
+		rel->rd_amcache = NULL;
+	}
+}
+
 /*
  * Return the current size of `rel` in bytes. If `forkNumber` is
  * InvalidForkNumber, return the relation's overall size, otherwise the size
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 87002049538..69557fc7a2c 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -221,10 +221,12 @@ typedef struct RelationData
 	 * rd_amcache is available for index and table AMs to cache private data
 	 * about the relation.  This must be just a cache since it may get reset
 	 * at any time (in particular, it will get reset by a relcache inval
-	 * message for the relation).  If used, it must point to a single memory
-	 * chunk palloc'd in CacheMemoryContext, or in rd_indexcxt for an index
-	 * relation.  A relcache reset will include freeing that chunk and setting
-	 * rd_amcache = NULL.
+	 * message for the relation).  If used for table AM it must point to a
+	 * single memory chunk palloc'd in CacheMemoryContext, or more complex
+	 * data structure in that memory context to be freed by free_rd_amcache
+	 * method. If used for index AM it must point to a single memory chunk
+	 * palloc'd in rd_indexcxt memory context.  A relcache reset will include
+	 * freeing that chunk and setting rd_amcache = NULL.
 	 */
 	void	   *rd_amcache;		/* available for use by index/table AM */
 
-- 
2.39.3 (Apple Git-145)

