From 4a2972855e12b245e4eb914ff1bebf1d6484fef4 Mon Sep 17 00:00:00 2001
From: Melih Mutlu <m.melihmutlu@gmail.com>
Date: Tue, 13 Jun 2023 16:43:24 +0300
Subject: [PATCH v5 1/2] Separate memory contexts for caches

This patch introduces new memory contexts under CacheMemoryContext
for different types of caches such as CatCacheContext and
RelCacheContext. Most of the time relcache and catcache constitutes
a large part of CacheMemoryContext and this can make memory usages from
other caches unvisible. Having separate contexts aims to help to
understand more about memory usage of cache related contexts.
---
 .vscode/c_cpp_properties.json               |  16 +++
 .vscode/launch.json                         |  68 +++++++++++++
 .vscode/settings.json                       |  62 ++++++++++++
 .vscode/tasks.json                          |  28 ++++++
 src/backend/catalog/namespace.c             |   5 +-
 src/backend/commands/policy.c               |   4 +-
 src/backend/commands/tablecmds.c            |   2 +-
 src/backend/commands/trigger.c              |   4 +-
 src/backend/executor/spi.c                  |  26 ++++-
 src/backend/foreign/foreign.c               |   4 +-
 src/backend/partitioning/partdesc.c         |   6 +-
 src/backend/replication/pgoutput/pgoutput.c |  20 +++-
 src/backend/utils/adt/pg_locale.c           |   6 +-
 src/backend/utils/cache/attoptcache.c       |   8 +-
 src/backend/utils/cache/catcache.c          |  41 +++++---
 src/backend/utils/cache/partcache.c         |  10 +-
 src/backend/utils/cache/plancache.c         |  38 +++++---
 src/backend/utils/cache/relcache.c          | 102 +++++++++++---------
 src/backend/utils/cache/spccache.c          |   7 +-
 src/backend/utils/cache/ts_cache.c          |  49 ++++++----
 src/backend/utils/cache/typcache.c          |  64 +++++++-----
 src/include/utils/relcache.h                |   3 +
 22 files changed, 428 insertions(+), 145 deletions(-)
 create mode 100755 .vscode/c_cpp_properties.json
 create mode 100755 .vscode/launch.json
 create mode 100755 .vscode/settings.json
 create mode 100755 .vscode/tasks.json

diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json
new file mode 100755
index 0000000000..4039befa76
--- /dev/null
+++ b/.vscode/c_cpp_properties.json
@@ -0,0 +1,16 @@
+{
+    "configurations": [
+        {
+            "name": "Linux",
+            "includePath": [
+                "${workspaceFolder}/**"
+            ],
+            "defines": [],
+            "compilerPath": "/usr/bin/gcc",
+            "cStandard": "c17",
+            "cppStandard": "gnu++17",
+            "intelliSenseMode": "linux-gcc-x64"
+        }
+    ],
+    "version": 4
+}
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100755
index 0000000000..4fc22e17f0
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,68 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+
+        {
+            "name": "(gdb) Attach",
+            "type": "cppdbg",
+            "request": "attach",
+            "program": "/home/melih/build/src/backend/postgres",
+            "MIMode": "gdb",
+            "setupCommands": [
+                {
+                    "description": "Enable pretty-printing for gdb",
+                    "text": "-enable-pretty-printing",
+                    "ignoreFailures": true
+                },
+                {
+                    "description": "Set Disassembly Flavor to Intel",
+                    "text": "-gdb-set disassembly-flavor intel",
+                    "ignoreFailures": true
+                }
+            ]
+        },
+
+        {
+            "name": "(gdb) psql Attach",
+            "type": "cppdbg",
+            "request": "attach",
+            "program": "/home/melih/build/src/bin/psql/psql",
+            "MIMode": "gdb",
+            "setupCommands": [
+                {
+                    "description": "Enable pretty-printing for gdb",
+                    "text": "-enable-pretty-printing",
+                    "ignoreFailures": true
+                },
+                {
+                    "description": "Set Disassembly Flavor to Intel",
+                    "text": "-gdb-set disassembly-flavor intel",
+                    "ignoreFailures": true
+                }
+            ]
+        },
+
+        {
+            "name": "(gdb) pg_basebackup Attach",
+            "type": "cppdbg",
+            "request": "attach",
+            "program": "/home/melih/build/src/bin/pg_basebackup/pg_basebackup",
+            "MIMode": "gdb",
+            "setupCommands": [
+                {
+                    "description": "Enable pretty-printing for gdb",
+                    "text": "-enable-pretty-printing",
+                    "ignoreFailures": true
+                },
+                {
+                    "description": "Set Disassembly Flavor to Intel",
+                    "text": "-gdb-set disassembly-flavor intel",
+                    "ignoreFailures": true
+                }
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100755
index 0000000000..cdc8495d97
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,62 @@
+{
+    "files.associations": {
+        "errno.h": "c",
+        "pqformat.h": "c",
+        "stringinfo.h": "c",
+        "libpq-be.h": "c",
+        "latch.h": "c",
+        "in.h": "c",
+        "array": "c",
+        "string": "c",
+        "string_view": "c",
+        "bitset": "c",
+        "initializer_list": "c",
+        "utility": "c",
+        "random": "cpp",
+        "libpq-fe.h": "c",
+        "fmgr.h": "c",
+        "libpq.h": "c",
+        "protocol.h": "c",
+        "atomic": "c",
+        "bit": "c",
+        "*.tcc": "c",
+        "chrono": "c",
+        "cmath": "c",
+        "compare": "c",
+        "concepts": "c",
+        "condition_variable": "c",
+        "cstddef": "c",
+        "deque": "c",
+        "list": "c",
+        "unordered_map": "c",
+        "vector": "c",
+        "exception": "c",
+        "functional": "c",
+        "iterator": "c",
+        "memory": "c",
+        "memory_resource": "c",
+        "numeric": "c",
+        "ratio": "c",
+        "system_error": "c",
+        "type_traits": "c",
+        "algorithm": "c",
+        "limits": "c",
+        "mutex": "c",
+        "ostream": "c",
+        "semaphore": "c",
+        "shared_mutex": "c",
+        "stop_token": "c",
+        "streambuf": "c",
+        "thread": "c",
+        "tuple": "c",
+        "typeinfo": "c",
+        "*.inc": "c",
+        "shmem.h": "c",
+        "walreceiver.h": "c",
+        "bgwriter.h": "c",
+        "xloginsert.h": "c",
+        "pg_bswap.h": "c",
+        "varatt.h": "c",
+        "varlena.h": "c"
+    }
+}
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100755
index 0000000000..08d9005bbb
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,28 @@
+{
+    "tasks": [
+        {
+            "type": "cppbuild",
+            "label": "C/C++: gcc build active file",
+            "command": "/usr/bin/gcc",
+            "args": [
+                "-fdiagnostics-color=always",
+                "-g",
+                "${file}",
+                "-o",
+                "${fileDirname}/${fileBasenameNoExtension}"
+            ],
+            "options": {
+                "cwd": "${fileDirname}"
+            },
+            "problemMatcher": [
+                "$gcc"
+            ],
+            "group": {
+                "kind": "build",
+                "isDefault": true
+            },
+            "detail": "Task generated by Debugger."
+        }
+    ],
+    "version": "2.0.0"
+}
\ No newline at end of file
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index d97d632a7e..04f7d5051c 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -321,8 +321,11 @@ spcache_init(void)
 
 	if (SearchPathCacheContext == NULL)
 	{
+		if (!CacheMemoryContext)
+			CreateCacheMemoryContext();
+
 		/* Make the context we'll keep search path cache hashtable in */
-		SearchPathCacheContext = AllocSetContextCreate(TopMemoryContext,
+		SearchPathCacheContext = AllocSetContextCreate(CacheMemoryContext,
 													   "search_path processing cache",
 													   ALLOCSET_DEFAULT_SIZES);
 	}
diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c
index 83056960fe..1c32ff8889 100644
--- a/src/backend/commands/policy.c
+++ b/src/backend/commands/policy.c
@@ -313,10 +313,10 @@ RelationBuildRowSecurity(Relation relation)
 
 	/*
 	 * Success.  Reparent the descriptor's memory context under
-	 * CacheMemoryContext so that it will live indefinitely, then attach the
+	 * RelCacheContext so that it will live indefinitely, then attach the
 	 * policy descriptor to the relcache entry.
 	 */
-	MemoryContextSetParent(rscxt, CacheMemoryContext);
+	MemoryContextSetParent(rscxt, RelCacheContext);
 
 	relation->rd_rsdesc = rsdesc;
 }
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 5823fce934..d33206f2eb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -18333,7 +18333,7 @@ register_on_commit_action(Oid relid, OnCommitAction action)
 	if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
 		return;
 
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(RelCacheContext);
 
 	oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
 	oc->relid = relid;
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 97c087929f..837ceb8ee7 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -1851,7 +1851,7 @@ EnableDisableTrigger(Relation rel, const char *tgname, Oid tgparent,
  * Build trigger data to attach to the given relcache entry.
  *
  * Note that trigger data attached to a relcache entry must be stored in
- * CacheMemoryContext to ensure it survives as long as the relcache entry.
+ * RelCacheContext to ensure it survives as long as the relcache entry.
  * But we should be running in a less long-lived working context.  To avoid
  * leaking cache memory if this routine fails partway through, we build a
  * temporary TriggerDesc in working memory and then copy the completed
@@ -1998,7 +1998,7 @@ RelationBuildTriggers(Relation relation)
 		SetTriggerFlags(trigdesc, &(triggers[i]));
 
 	/* Copy completed trigdesc into cache storage */
-	oldContext = MemoryContextSwitchTo(CacheMemoryContext);
+	oldContext = MemoryContextSwitchTo(RelCacheContext);
 	relation->trigdesc = CopyTriggerDesc(trigdesc);
 	MemoryContextSwitchTo(oldContext);
 
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index ecb2e4ccaa..60a3a5ed64 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -50,6 +50,8 @@ static _SPI_connection *_SPI_current = NULL;
 static int	_SPI_stack_depth = 0;	/* allocated size of _SPI_stack */
 static int	_SPI_connected = -1;	/* current stack index */
 
+static MemoryContext SPICacheContext = NULL;
+
 typedef struct SPICallbackArg
 {
 	const char *query;
@@ -972,6 +974,16 @@ SPI_prepare_params(const char *src,
 	return result;
 }
 
+static void
+CreateSPICacheContext(void)
+{
+	/*
+	 * SPI does not have a separate memory context, CacheMemoryContext is used.
+	 */
+	if (!SPICacheContext)
+		SPICacheContext = CacheMemoryContext;
+}
+
 int
 SPI_keepplan(SPIPlanPtr plan)
 {
@@ -981,13 +993,16 @@ SPI_keepplan(SPIPlanPtr plan)
 		plan->saved || plan->oneshot)
 		return SPI_ERROR_ARGUMENT;
 
+	if (!SPICacheContext)
+		CreateSPICacheContext();
+
 	/*
-	 * Mark it saved, reparent it under CacheMemoryContext, and mark all the
+	 * Mark it saved, reparent it under SPICacheContext, and mark all the
 	 * component CachedPlanSources as saved.  This sequence cannot fail
 	 * partway through, so there's no risk of long-term memory leakage.
 	 */
 	plan->saved = true;
-	MemoryContextSetParent(plan->plancxt, CacheMemoryContext);
+	MemoryContextSetParent(plan->plancxt, SPICacheContext);
 
 	foreach(lc, plan->plancache_list)
 	{
@@ -3255,13 +3270,16 @@ _SPI_save_plan(SPIPlanPtr plan)
 
 	MemoryContextSwitchTo(oldcxt);
 
+	if (!SPICacheContext)
+		CreateSPICacheContext();
+
 	/*
-	 * Mark it saved, reparent it under CacheMemoryContext, and mark all the
+	 * Mark it saved, reparent it under SPICacheContext, and mark all the
 	 * component CachedPlanSources as saved.  This sequence cannot fail
 	 * partway through, so there's no risk of long-term memory leakage.
 	 */
 	newplan->saved = true;
-	MemoryContextSetParent(newplan->plancxt, CacheMemoryContext);
+	MemoryContextSetParent(newplan->plancxt, SPICacheContext);
 
 	foreach(lc, newplan->plancache_list)
 	{
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index f0835fc307..cd544d3b3a 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -449,8 +449,8 @@ GetFdwRoutineForRelation(Relation relation, bool makecopy)
 		/* Get the info by consulting the catalogs and the FDW code */
 		fdwroutine = GetFdwRoutineByRelId(RelationGetRelid(relation));
 
-		/* Save the data for later reuse in CacheMemoryContext */
-		cfdwroutine = (FdwRoutine *) MemoryContextAlloc(CacheMemoryContext,
+		/* Save the data for later reuse in RelCacheContext */
+		cfdwroutine = (FdwRoutine *) MemoryContextAlloc(RelCacheContext,
 														sizeof(FdwRoutine));
 		memcpy(cfdwroutine, fdwroutine, sizeof(FdwRoutine));
 		relation->rd_fdwroutine = cfdwroutine;
diff --git a/src/backend/partitioning/partdesc.c b/src/backend/partitioning/partdesc.c
index 328b4d450e..a59ca34200 100644
--- a/src/backend/partitioning/partdesc.c
+++ b/src/backend/partitioning/partdesc.c
@@ -115,10 +115,10 @@ RelationGetPartitionDesc(Relation rel, bool omit_detached)
  *
  * Partition descriptor is a complex structure; to avoid complicated logic to
  * free individual elements whenever the relcache entry is flushed, we give it
- * its own memory context, a child of CacheMemoryContext, which can easily be
+ * its own memory context, a child of RelCacheContext, which can easily be
  * deleted on its own.  To avoid leaking memory in that context in case of an
  * error partway through this function, the context is initially created as a
- * child of CurTransactionContext and only re-parented to CacheMemoryContext
+ * child of CurTransactionContext and only re-parented to RelCacheContext
  * at the end, when no further errors are possible.  Also, we don't make this
  * context the current context except in very brief code sections, out of fear
  * that some of our callees allocate memory on their own which would be leaked
@@ -373,7 +373,7 @@ retry:
 	 * We have a fully valid partdesc.  Reparent it so that it has the right
 	 * lifespan.
 	 */
-	MemoryContextSetParent(new_pdcxt, CacheMemoryContext);
+	MemoryContextSetParent(new_pdcxt, RelCacheContext);
 
 	/*
 	 * Store it into relcache.
diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c
index 7d464f656a..a1dcc1fdd7 100644
--- a/src/backend/replication/pgoutput/pgoutput.c
+++ b/src/backend/replication/pgoutput/pgoutput.c
@@ -216,6 +216,8 @@ typedef struct PGOutputTxnData
 /* Map used to remember which relation schemas we sent. */
 static HTAB *RelationSyncCache = NULL;
 
+static MemoryContext PgOutputCacheContext = NULL;
+
 static void init_rel_sync_cache(MemoryContext cachectx);
 static void cleanup_rel_sync_cache(TransactionId xid, bool is_commit);
 static RelationSyncEntry *get_rel_sync_entry(PGOutputData *data,
@@ -421,6 +423,17 @@ parse_output_parameters(List *options, PGOutputData *data)
 				errmsg("option \"%s\" missing", "publication_names"));
 }
 
+static void
+CreatePgOutputCacheContext(void)
+{
+	/*
+	 * PgOutput does not have a separate memory context, CacheMemoryContext is
+	 * used.
+	 */
+	if (!PgOutputCacheContext)
+		PgOutputCacheContext = CacheMemoryContext;
+}
+
 /*
  * Initialize this plugin
  */
@@ -449,6 +462,9 @@ pgoutput_startup(LogicalDecodingContext *ctx, OutputPluginOptions *opt,
 	/* This plugin uses binary protocol. */
 	opt->output_type = OUTPUT_PLUGIN_BINARY_OUTPUT;
 
+	if (!PgOutputCacheContext)
+		CreatePgOutputCacheContext();
+
 	/*
 	 * This is replication start and not slot initialization.
 	 *
@@ -535,7 +551,7 @@ pgoutput_startup(LogicalDecodingContext *ctx, OutputPluginOptions *opt,
 		}
 
 		/* Initialize relation schema cache. */
-		init_rel_sync_cache(CacheMemoryContext);
+		init_rel_sync_cache(PgOutputCacheContext);
 	}
 	else
 	{
@@ -2003,7 +2019,7 @@ set_schema_sent_in_streamed_txn(RelationSyncEntry *entry, TransactionId xid)
 {
 	MemoryContext oldctx;
 
-	oldctx = MemoryContextSwitchTo(CacheMemoryContext);
+	oldctx = MemoryContextSwitchTo(PgOutputCacheContext);
 
 	entry->streamed_txns = lappend_xid(entry->streamed_txns, xid);
 
diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index 7d92f580a5..e573a435b6 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -62,6 +62,7 @@
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "utils/builtins.h"
+#include "utils/catcache.h"
 #include "utils/formatting.h"
 #include "utils/guc_hooks.h"
 #include "utils/lsyscache.h"
@@ -1345,7 +1346,10 @@ pg_newlocale_from_collation(Oid collid)
 
 	if (CollationCache == NULL)
 	{
-		CollationCacheContext = AllocSetContextCreate(TopMemoryContext,
+		if (!CacheMemoryContext)
+			CreateCacheMemoryContext();
+
+		CollationCacheContext = AllocSetContextCreate(CacheMemoryContext,
 													  "collation cache",
 													  ALLOCSET_DEFAULT_SIZES);
 		CollationCache = collation_cache_create(CollationCacheContext,
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index 5c8360c08b..9709199727 100644
--- a/src/backend/utils/cache/attoptcache.c
+++ b/src/backend/utils/cache/attoptcache.c
@@ -113,9 +113,9 @@ InitializeAttoptCache(void)
 		hash_create("Attopt cache", 256, &ctl,
 					HASH_ELEM | HASH_FUNCTION);
 
-	/* Make sure we've initialized CacheMemoryContext. */
-	if (!CacheMemoryContext)
-		CreateCacheMemoryContext();
+	/* Make sure we've initialized RelCacheContext. */
+	if (!RelCacheContext)
+		CreateRelCacheContext();
 
 	/* Watch for invalidation events. */
 	CacheRegisterSyscacheCallback(ATTNUM,
@@ -178,7 +178,7 @@ get_attribute_options(Oid attrelid, int attnum)
 			{
 				bytea	   *bytea_opts = attribute_reloptions(datum, false);
 
-				opts = MemoryContextAlloc(CacheMemoryContext,
+				opts = MemoryContextAlloc(RelCacheContext,
 										  VARSIZE(bytea_opts));
 				memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
 			}
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 9ad7681f15..7c6a755e35 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -83,6 +83,9 @@ static CatCInProgress *catcache_in_progress_stack = NULL;
 /* Cache management header --- pointer is NULL until created */
 static CatCacheHeader *CacheHdr = NULL;
 
+static MemoryContext CatCacheContext = NULL;
+static void CreateCatCacheContext(void);
+
 static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
 											   int nkeys,
 											   Datum v1, Datum v2,
@@ -122,7 +125,6 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
 static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
 							 Datum *srckeys, Datum *dstkeys);
 
-
 /*
  *					internal support functions
  */
@@ -717,6 +719,17 @@ CreateCacheMemoryContext(void)
 												   ALLOCSET_DEFAULT_SIZES);
 }
 
+static void
+CreateCatCacheContext(void)
+{
+	if (!CacheMemoryContext)
+		CreateCacheMemoryContext();
+
+	if (!CatCacheContext)
+		CatCacheContext = AllocSetContextCreate(CacheMemoryContext,
+													  "CatCacheContext",
+													   ALLOCSET_DEFAULT_SIZES);
+}
 
 /*
  *		ResetCatalogCache
@@ -903,10 +916,10 @@ InitCatCache(int id,
 	 * first switch to the cache context so our allocations do not vanish at
 	 * the end of a transaction
 	 */
-	if (!CacheMemoryContext)
-		CreateCacheMemoryContext();
+	if (!CatCacheContext)
+		CreateCatCacheContext();
 
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(CatCacheContext);
 
 	/*
 	 * if first time through, initialize the cache group header
@@ -993,7 +1006,7 @@ RehashCatCache(CatCache *cp)
 
 	/* Allocate a new, larger, hash table. */
 	newnbuckets = cp->cc_nbuckets * 2;
-	newbucket = (dlist_head *) MemoryContextAllocZero(CacheMemoryContext, newnbuckets * sizeof(dlist_head));
+	newbucket = (dlist_head *) MemoryContextAllocZero(CatCacheContext, newnbuckets * sizeof(dlist_head));
 
 	/* Move all entries from old hash table to new. */
 	for (i = 0; i < cp->cc_nbuckets; i++)
@@ -1031,7 +1044,7 @@ RehashCatCacheLists(CatCache *cp)
 
 	/* Allocate a new, larger, hash table. */
 	newnbuckets = cp->cc_nlbuckets * 2;
-	newbucket = (dlist_head *) MemoryContextAllocZero(CacheMemoryContext, newnbuckets * sizeof(dlist_head));
+	newbucket = (dlist_head *) MemoryContextAllocZero(CatCacheContext, newnbuckets * sizeof(dlist_head));
 
 	/* Move all entries from old hash table to new. */
 	for (i = 0; i < cp->cc_nlbuckets; i++)
@@ -1098,9 +1111,9 @@ CatalogCacheInitializeCache(CatCache *cache)
 	 * switch to the cache context so our allocations do not vanish at the end
 	 * of a transaction
 	 */
-	Assert(CacheMemoryContext != NULL);
+	Assert(CatCacheContext != NULL);
 
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(CatCacheContext);
 
 	/*
 	 * copy the relcache's tuple descriptor to permanent cache storage
@@ -1161,7 +1174,7 @@ CatalogCacheInitializeCache(CatCache *cache)
 		 */
 		fmgr_info_cxt(eqfunc,
 					  &cache->cc_skey[i].sk_func,
-					  CacheMemoryContext);
+					  CatCacheContext);
 
 		/* Initialize sk_attno suitably for HeapKeyTest() and heap scans */
 		cache->cc_skey[i].sk_attno = cache->cc_keyno[i];
@@ -1746,7 +1759,7 @@ SearchCatCacheList(CatCache *cache,
 		int			nbuckets = 16;
 
 		cache->cc_lbucket = (dlist_head *)
-			MemoryContextAllocZero(CacheMemoryContext,
+			MemoryContextAllocZero(CatCacheContext,
 								   nbuckets * sizeof(dlist_head));
 		/* Don't set cc_nlbuckets if we get OOM allocating cc_lbucket */
 		cache->cc_nlbuckets = nbuckets;
@@ -1977,7 +1990,7 @@ SearchCatCacheList(CatCache *cache,
 		ResourceOwnerEnlarge(CurrentResourceOwner);
 
 		/* Now we can build the CatCList entry. */
-		oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+		oldcxt = MemoryContextSwitchTo(CatCacheContext);
 		nmembers = list_length(ctlist);
 		cl = (CatCList *)
 			palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *));
@@ -2178,7 +2191,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
 			dtp = ntp;
 
 		/* Allocate memory for CatCTup and the cached tuple in one go */
-		oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+		oldcxt = MemoryContextSwitchTo(CatCacheContext);
 
 		ct = (CatCTup *) palloc(sizeof(CatCTup) +
 								MAXIMUM_ALIGNOF + dtp->t_len);
@@ -2213,7 +2226,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
 	else
 	{
 		/* Set up keys for a negative cache entry */
-		oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+		oldcxt = MemoryContextSwitchTo(CatCacheContext);
 		ct = (CatCTup *) palloc(sizeof(CatCTup));
 
 		/*
@@ -2449,4 +2462,4 @@ ResOwnerPrintCatCacheList(Datum res)
 	return psprintf("cache %s (%d), list %p has count %d",
 					list->my_cache->cc_relname, list->my_cache->id,
 					list, list->refcount);
-}
+}
\ No newline at end of file
diff --git a/src/backend/utils/cache/partcache.c b/src/backend/utils/cache/partcache.c
index f5d7d70def..aed33576f0 100644
--- a/src/backend/utils/cache/partcache.c
+++ b/src/backend/utils/cache/partcache.c
@@ -65,11 +65,11 @@ RelationGetPartitionKey(Relation rel)
  *
  * Partitioning key data is a complex structure; to avoid complicated logic to
  * free individual elements whenever the relcache entry is flushed, we give it
- * its own memory context, a child of CacheMemoryContext, which can easily be
+ * its own memory context, a child of RelCacheContext, which can easily be
  * deleted on its own.  To avoid leaking memory in that context in case of an
  * error partway through this function, the context is initially created as a
- * child of CurTransactionContext and only re-parented to CacheMemoryContext
- * at the end, when no further errors are possible.  Also, we don't make this
+ * child of CurTransactionContext and only re-parented to RelCacheContext at
+ * the end, when no further errors are possible.  Also, we don't make this
  * context the current context except in very brief code sections, out of fear
  * that some of our callees allocate memory on their own which would be leaked
  * permanently.
@@ -263,7 +263,7 @@ RelationBuildPartitionKey(Relation relation)
 	 * Success --- reparent our context and make the relcache point to the
 	 * newly constructed key
 	 */
-	MemoryContextSetParent(partkeycxt, CacheMemoryContext);
+	MemoryContextSetParent(partkeycxt, RelCacheContext);
 	relation->rd_partkeycxt = partkeycxt;
 	relation->rd_partkey = key;
 }
@@ -411,7 +411,7 @@ generate_partition_qual(Relation rel)
 	 */
 	if (result != NIL)
 	{
-		rel->rd_partcheckcxt = AllocSetContextCreate(CacheMemoryContext,
+		rel->rd_partcheckcxt = AllocSetContextCreate(RelCacheContext,
 													 "partition constraint",
 													 ALLOCSET_SMALL_SIZES);
 		MemoryContextCopyAndSetIdentifier(rel->rd_partcheckcxt,
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index 55db8f5370..362180109d 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -99,6 +99,8 @@ static dlist_head saved_plan_list = DLIST_STATIC_INIT(saved_plan_list);
  */
 static dlist_head cached_expression_list = DLIST_STATIC_INIT(cached_expression_list);
 
+static MemoryContext PlanCacheContext = NULL;
+
 static void ReleaseGenericPlan(CachedPlanSource *plansource);
 static List *RevalidateCachedQuery(CachedPlanSource *plansource,
 								   QueryEnvironment *queryEnv);
@@ -463,10 +465,21 @@ CompleteCachedPlan(CachedPlanSource *plansource,
 	plansource->is_valid = true;
 }
 
+static void
+CreatePlanCacheContext(void)
+{
+	/*
+	 * Plan cache does not have a separate memory context, CacheMemoryContext
+	 * is used.
+	 */
+	if (!PlanCacheContext)
+		PlanCacheContext = CacheMemoryContext;
+}
+
 /*
  * SaveCachedPlan: save a cached plan permanently
  *
- * This function moves the cached plan underneath CacheMemoryContext (making
+ * This function moves the cached plan underneath PlanCacheContext (making
  * it live for the life of the backend, unless explicitly dropped), and adds
  * it to the list of cached plans that are checked for invalidation when an
  * sinval event occurs.
@@ -493,18 +506,21 @@ SaveCachedPlan(CachedPlanSource *plansource)
 	/*
 	 * In typical use, this function would be called before generating any
 	 * plans from the CachedPlanSource.  If there is a generic plan, moving it
-	 * into CacheMemoryContext would be pretty risky since it's unclear
+	 * into PlanCacheContext would be pretty risky since it's unclear
 	 * whether the caller has taken suitable care with making references
 	 * long-lived.  Best thing to do seems to be to discard the plan.
 	 */
 	ReleaseGenericPlan(plansource);
 
+	if (!PlanCacheContext)
+		CreatePlanCacheContext();
+
 	/*
-	 * Reparent the source memory context under CacheMemoryContext so that it
+	 * Reparent the source memory context under PlanCacheContext so that it
 	 * will live indefinitely.  The query_context follows along since it's
 	 * already a child of the other one.
 	 */
-	MemoryContextSetParent(plansource->context, CacheMemoryContext);
+	MemoryContextSetParent(plansource->context, PlanCacheContext);
 
 	/*
 	 * Add the entry to the global list of cached plans.
@@ -1205,8 +1221,8 @@ GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams,
 			/* Immediately reparent into appropriate context */
 			if (plansource->is_saved)
 			{
-				/* saved plans all live under CacheMemoryContext */
-				MemoryContextSetParent(plan->context, CacheMemoryContext);
+				/* saved plans all live under PlanCacheContext */
+				MemoryContextSetParent(plan->context, PlanCacheContext);
 				plan->is_saved = true;
 			}
 			else
@@ -1262,14 +1278,14 @@ GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams,
 		ResourceOwnerRememberPlanCacheRef(owner, plan);
 
 	/*
-	 * Saved plans should be under CacheMemoryContext so they will not go away
+	 * Saved plans should be under PlanCacheContext so they will not go away
 	 * until their reference count goes to zero.  In the generic-plan cases we
 	 * already took care of that, but for a custom plan, do it as soon as we
 	 * have created a reference-counted link.
 	 */
 	if (customplan && plansource->is_saved)
 	{
-		MemoryContextSetParent(plan->context, CacheMemoryContext);
+		MemoryContextSetParent(plan->context, PlanCacheContext);
 		plan->is_saved = true;
 	}
 
@@ -1492,7 +1508,7 @@ CachedPlanIsSimplyValid(CachedPlanSource *plansource, CachedPlan *plan,
  * CachedPlanSetParentContext: move a CachedPlanSource to a new memory context
  *
  * This can only be applied to unsaved plans; once saved, a plan always
- * lives underneath CacheMemoryContext.
+ * lives underneath PlanCacheContext.
  */
 void
 CachedPlanSetParentContext(CachedPlanSource *plansource,
@@ -1713,10 +1729,10 @@ GetCachedExpression(Node *expr)
 	MemoryContextSwitchTo(oldcxt);
 
 	/*
-	 * Reparent the expr's memory context under CacheMemoryContext so that it
+	 * Reparent the expr's memory context under PlanCacheContext so that it
 	 * will live indefinitely.
 	 */
-	MemoryContextSetParent(cexpr_context, CacheMemoryContext);
+	MemoryContextSetParent(cexpr_context, PlanCacheContext);
 
 	/*
 	 * Add the entry to the global list of cached expressions.
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 398114373e..82e25b5ac3 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -145,6 +145,8 @@ bool		criticalRelcachesBuilt = false;
  */
 bool		criticalSharedRelcachesBuilt = false;
 
+MemoryContext RelCacheContext = NULL;
+
 /*
  * This counter counts relcache inval events received since backend startup
  * (but only for rels that are actually in cache).  Presently, we use it only
@@ -270,7 +272,6 @@ typedef struct opclasscacheent
 
 static HTAB *OpClassCache = NULL;
 
-
 /* non-export function prototypes */
 
 static void RelationCloseCleanup(Relation relation);
@@ -321,7 +322,6 @@ static OpClassCacheEnt *LookupOpclassInfo(Oid operatorClassOid,
 static void RelationCacheInitFileRemoveInDir(const char *tblspcpath);
 static void unlink_initfile(const char *initfilename, int elevel);
 
-
 /*
  *		ScanPgRelation
  *
@@ -413,8 +413,8 @@ AllocateRelationDesc(Form_pg_class relp)
 	MemoryContext oldcxt;
 	Form_pg_class relationForm;
 
-	/* Relcache entries must live in CacheMemoryContext */
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	/* Relcache entries must live in RelCacheContext */
+	oldcxt = MemoryContextSwitchTo(RelCacheContext);
 
 	/*
 	 * allocate and zero space for new relation descriptor
@@ -498,14 +498,14 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
 	options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptsfn);
 
 	/*
-	 * Copy parsed data into CacheMemoryContext.  To guard against the
+	 * Copy parsed data into RelCacheContext.  To guard against the
 	 * possibility of leaks in the reloptions code, we want to do the actual
 	 * parsing in the caller's memory context and copy the results into
-	 * CacheMemoryContext after the fact.
+	 * RelCacheContext after the fact.
 	 */
 	if (options)
 	{
-		relation->rd_options = MemoryContextAlloc(CacheMemoryContext,
+		relation->rd_options = MemoryContextAlloc(RelCacheContext,
 												  VARSIZE(options));
 		memcpy(relation->rd_options, options, VARSIZE(options));
 		pfree(options);
@@ -535,7 +535,7 @@ RelationBuildTupleDesc(Relation relation)
 		relation->rd_rel->reltype ? relation->rd_rel->reltype : RECORDOID;
 	relation->rd_att->tdtypmod = -1;	/* just to be sure */
 
-	constr = (TupleConstr *) MemoryContextAllocZero(CacheMemoryContext,
+	constr = (TupleConstr *) MemoryContextAllocZero(RelCacheContext,
 													sizeof(TupleConstr));
 
 	/*
@@ -618,7 +618,7 @@ RelationBuildTupleDesc(Relation relation)
 
 				if (attrmiss == NULL)
 					attrmiss = (AttrMissing *)
-						MemoryContextAllocZero(CacheMemoryContext,
+						MemoryContextAllocZero(RelCacheContext,
 											   relation->rd_rel->relnatts *
 											   sizeof(AttrMissing));
 
@@ -639,7 +639,7 @@ RelationBuildTupleDesc(Relation relation)
 				else
 				{
 					/* otherwise copy in the correct context */
-					oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+					oldcxt = MemoryContextSwitchTo(RelCacheContext);
 					attrmiss[attnum - 1].am_value = datumCopy(missval,
 															  attp->attbyval,
 															  attp->attlen);
@@ -737,7 +737,7 @@ RelationBuildRuleLock(Relation relation)
 	/*
 	 * Make the private context.  Assume it'll not contain much data.
 	 */
-	rulescxt = AllocSetContextCreate(CacheMemoryContext,
+	rulescxt = AllocSetContextCreate(RelCacheContext,
 									 "relation rules",
 									 ALLOCSET_SMALL_SIZES);
 	relation->rd_rulescxt = rulescxt;
@@ -1440,7 +1440,7 @@ RelationInitIndexAccessInfo(Relation relation)
 	if (!HeapTupleIsValid(tuple))
 		elog(ERROR, "cache lookup failed for index %u",
 			 RelationGetRelid(relation));
-	oldcontext = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcontext = MemoryContextSwitchTo(RelCacheContext);
 	relation->rd_indextuple = heap_copytuple(tuple);
 	relation->rd_index = (Form_pg_index) GETSTRUCT(relation->rd_indextuple);
 	MemoryContextSwitchTo(oldcontext);
@@ -1469,7 +1469,7 @@ RelationInitIndexAccessInfo(Relation relation)
 	 * a context, and not just a couple of pallocs, is so that we won't leak
 	 * any subsidiary info attached to fmgr lookup records.
 	 */
-	indexcxt = AllocSetContextCreate(CacheMemoryContext,
+	indexcxt = AllocSetContextCreate(RelCacheContext,
 									 "index info",
 									 ALLOCSET_SMALL_SIZES);
 	relation->rd_indexcxt = indexcxt;
@@ -1614,6 +1614,18 @@ IndexSupportInitialize(oidvector *indclass,
 	}
 }
 
+void
+CreateRelCacheContext(void)
+{
+	if (!CacheMemoryContext)
+		CreateCacheMemoryContext();
+
+	if (!RelCacheContext)
+		RelCacheContext = AllocSetContextCreate(CacheMemoryContext,
+													  "RelCacheContext",
+													   ALLOCSET_DEFAULT_SIZES);
+}
+
 /*
  * LookupOpclassInfo
  *
@@ -1651,9 +1663,9 @@ LookupOpclassInfo(Oid operatorClassOid,
 		/* First time through: initialize the opclass cache */
 		HASHCTL		ctl;
 
-		/* Also make sure CacheMemoryContext exists */
-		if (!CacheMemoryContext)
-			CreateCacheMemoryContext();
+		/* Also make sure RelCacheContext exists */
+		if (!RelCacheContext)
+			CreateRelCacheContext();
 
 		ctl.keysize = sizeof(Oid);
 		ctl.entrysize = sizeof(OpClassCacheEnt);
@@ -1700,7 +1712,7 @@ LookupOpclassInfo(Oid operatorClassOid,
 	 */
 	if (opcentry->supportProcs == NULL && numSupport > 0)
 		opcentry->supportProcs = (RegProcedure *)
-			MemoryContextAllocZero(CacheMemoryContext,
+			MemoryContextAllocZero(RelCacheContext,
 								   numSupport * sizeof(RegProcedure));
 
 	/*
@@ -1859,7 +1871,7 @@ RelationInitTableAccessMethod(Relation relation)
  * during bootstrap or before RelationCacheInitializePhase3 runs, and none of
  * these properties matter then...)
  *
- * NOTE: we assume we are already switched into CacheMemoryContext.
+ * NOTE: we assume we are already switched into RelCacheContext.
  */
 static void
 formrdesc(const char *relationName, Oid relationReltype,
@@ -3063,7 +3075,7 @@ RememberToFreeTupleDescAtEOX(TupleDesc td)
 	{
 		MemoryContext oldcxt;
 
-		oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+		oldcxt = MemoryContextSwitchTo(RelCacheContext);
 
 		EOXactTupleDescArray = (TupleDesc *) palloc(16 * sizeof(TupleDesc));
 		EOXactTupleDescArrayLen = 16;
@@ -3527,10 +3539,10 @@ RelationBuildLocalRelation(const char *relname,
 	/*
 	 * switch to the cache context to create the relcache entry.
 	 */
-	if (!CacheMemoryContext)
-		CreateCacheMemoryContext();
+	if (!RelCacheContext)
+		CreateRelCacheContext();
 
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(RelCacheContext);
 
 	/*
 	 * allocate a new relation descriptor and fill in basic state fields.
@@ -3660,7 +3672,7 @@ RelationBuildLocalRelation(const char *relname,
 
 	/*
 	 * RelationInitTableAccessMethod will do syscache lookups, so we mustn't
-	 * run it in CacheMemoryContext.  Fortunately, the remaining steps don't
+	 * run it in RelCacheContext.  Fortunately, the remaining steps don't
 	 * require a long-lived current context.
 	 */
 	MemoryContextSwitchTo(oldcxt);
@@ -3954,8 +3966,8 @@ RelationCacheInitialize(void)
 	/*
 	 * make sure cache memory context exists
 	 */
-	if (!CacheMemoryContext)
-		CreateCacheMemoryContext();
+	if (!RelCacheContext)
+		CreateRelCacheContext();
 
 	/*
 	 * create hashtable that indexes the relcache
@@ -3970,7 +3982,7 @@ RelationCacheInitialize(void)
 	 */
 	allocsize = 4;
 	in_progress_list =
-		MemoryContextAlloc(CacheMemoryContext,
+		MemoryContextAlloc(RelCacheContext,
 						   allocsize * sizeof(*in_progress_list));
 	in_progress_list_maxlen = allocsize;
 
@@ -4008,10 +4020,7 @@ RelationCacheInitializePhase2(void)
 	if (IsBootstrapProcessingMode())
 		return;
 
-	/*
-	 * switch to cache memory context
-	 */
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(RelCacheContext);
 
 	/*
 	 * Try to load the shared relcache cache file.  If unsuccessful, bootstrap
@@ -4063,10 +4072,7 @@ RelationCacheInitializePhase3(void)
 	 */
 	RelationMapInitializePhase3();
 
-	/*
-	 * switch to cache memory context
-	 */
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(RelCacheContext);
 
 	/*
 	 * Try to load the local relcache cache file.  If unsuccessful, bootstrap
@@ -4380,7 +4386,7 @@ BuildHardcodedDescriptor(int natts, const FormData_pg_attribute *attrs)
 	MemoryContext oldcxt;
 	int			i;
 
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(RelCacheContext);
 
 	result = CreateTemplateTupleDesc(natts);
 	result->tdtypeid = RECORDOID;	/* not right, but we don't care */
@@ -4450,7 +4456,7 @@ AttrDefaultFetch(Relation relation, int ndef)
 
 	/* Allocate array with room for as many entries as expected */
 	attrdef = (AttrDefault *)
-		MemoryContextAllocZero(CacheMemoryContext,
+		MemoryContextAllocZero(RelCacheContext,
 							   ndef * sizeof(AttrDefault));
 
 	/* Search pg_attrdef for relevant entries */
@@ -4489,7 +4495,7 @@ AttrDefaultFetch(Relation relation, int ndef)
 			char	   *s = TextDatumGetCString(val);
 
 			attrdef[found].adnum = adform->adnum;
-			attrdef[found].adbin = MemoryContextStrdup(CacheMemoryContext, s);
+			attrdef[found].adbin = MemoryContextStrdup(RelCacheContext, s);
 			pfree(s);
 			found++;
 		}
@@ -4546,7 +4552,7 @@ CheckConstraintFetch(Relation relation)
 
 	/* Allocate array with room for as many entries as expected */
 	check = (ConstrCheck *)
-		MemoryContextAllocZero(CacheMemoryContext,
+		MemoryContextAllocZero(RelCacheContext,
 							   ncheck * sizeof(ConstrCheck));
 
 	/* Search pg_constraint for relevant entries */
@@ -4580,7 +4586,7 @@ CheckConstraintFetch(Relation relation)
 		check[found].ccenforced = conform->conenforced;
 		check[found].ccvalid = conform->convalidated;
 		check[found].ccnoinherit = conform->connoinherit;
-		check[found].ccname = MemoryContextStrdup(CacheMemoryContext,
+		check[found].ccname = MemoryContextStrdup(RelCacheContext,
 												  NameStr(conform->conname));
 
 		/* Grab and test conbin is actually set */
@@ -4595,7 +4601,7 @@ CheckConstraintFetch(Relation relation)
 			/* detoast and convert to cstring in caller's context */
 			char	   *s = TextDatumGetCString(val);
 
-			check[found].ccbin = MemoryContextStrdup(CacheMemoryContext, s);
+			check[found].ccbin = MemoryContextStrdup(RelCacheContext, s);
 			pfree(s);
 			found++;
 		}
@@ -4712,7 +4718,7 @@ RelationGetFKeyList(Relation relation)
 	table_close(conrel, AccessShareLock);
 
 	/* Now save a copy of the completed list in the relcache entry. */
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(RelCacheContext);
 	oldlist = relation->rd_fkeylist;
 	relation->rd_fkeylist = copyObject(result);
 	relation->rd_fkeyvalid = true;
@@ -4855,7 +4861,7 @@ RelationGetIndexList(Relation relation)
 	list_sort(result, list_oid_cmp);
 
 	/* Now save a copy of the completed list in the relcache entry. */
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(RelCacheContext);
 	oldlist = relation->rd_indexlist;
 	relation->rd_indexlist = list_copy(result);
 	relation->rd_pkindex = pkeyIndex;
@@ -4947,7 +4953,7 @@ RelationGetStatExtList(Relation relation)
 	list_sort(result, list_oid_cmp);
 
 	/* Now save a copy of the completed list in the relcache entry. */
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(RelCacheContext);
 	oldlist = relation->rd_statlist;
 	relation->rd_statlist = list_copy(result);
 
@@ -5452,7 +5458,7 @@ restart:
 	 * leave the relcache entry looking like the other ones are valid but
 	 * empty.
 	 */
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(RelCacheContext);
 	relation->rd_keyattr = bms_copy(uindexattrs);
 	relation->rd_pkattr = bms_copy(pkindexattrs);
 	relation->rd_idattr = bms_copy(idindexattrs);
@@ -5552,7 +5558,7 @@ RelationGetIdentityKeyBitmap(Relation relation)
 	relation->rd_idattr = NULL;
 
 	/* Now save copy of the bitmap in the relcache entry */
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(RelCacheContext);
 	relation->rd_idattr = bms_copy(idindexattrs);
 	MemoryContextSwitchTo(oldcxt);
 
@@ -5881,7 +5887,7 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
 	}
 
 	/* Now save copy of the descriptor in the relcache entry. */
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(RelCacheContext);
 	relation->rd_pubdesc = palloc(sizeof(PublicationDesc));
 	memcpy(relation->rd_pubdesc, pubdesc, sizeof(PublicationDesc));
 	MemoryContextSwitchTo(oldcxt);
@@ -6084,7 +6090,7 @@ errtableconstraint(Relation rel, const char *conname)
  * criticalSharedRelcachesBuilt to true.
  * If not successful, return false.
  *
- * NOTE: we assume we are already switched into CacheMemoryContext.
+ * NOTE: we assume we are already switched into RelCacheContext.
  */
 static bool
 load_relcache_init_file(bool shared)
@@ -6255,7 +6261,7 @@ load_relcache_init_file(bool shared)
 			 * prepare index info context --- parameters should match
 			 * RelationInitIndexAccessInfo
 			 */
-			indexcxt = AllocSetContextCreate(CacheMemoryContext,
+			indexcxt = AllocSetContextCreate(RelCacheContext,
 											 "index info",
 											 ALLOCSET_SMALL_SIZES);
 			rel->rd_indexcxt = indexcxt;
diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/spccache.c
index 2345859929..6543f4df0f 100644
--- a/src/backend/utils/cache/spccache.c
+++ b/src/backend/utils/cache/spccache.c
@@ -86,9 +86,8 @@ InitializeTableSpaceCache(void)
 		hash_create("TableSpace cache", 16, &ctl,
 					HASH_ELEM | HASH_BLOBS);
 
-	/* Make sure we've initialized CacheMemoryContext. */
-	if (!CacheMemoryContext)
-		CreateCacheMemoryContext();
+	if (!RelCacheContext)
+		CreateRelCacheContext();
 
 	/* Watch for invalidation events. */
 	CacheRegisterSyscacheCallback(TABLESPACEOID,
@@ -151,7 +150,7 @@ get_tablespace(Oid spcid)
 		{
 			bytea	   *bytea_opts = tablespace_reloptions(datum, false);
 
-			opts = MemoryContextAlloc(CacheMemoryContext, VARSIZE(bytea_opts));
+			opts = MemoryContextAlloc(RelCacheContext, VARSIZE(bytea_opts));
 			memcpy(opts, bytea_opts, VARSIZE(bytea_opts));
 		}
 		ReleaseSysCache(tp);
diff --git a/src/backend/utils/cache/ts_cache.c b/src/backend/utils/cache/ts_cache.c
index 18cccd778f..e372874317 100644
--- a/src/backend/utils/cache/ts_cache.c
+++ b/src/backend/utils/cache/ts_cache.c
@@ -75,6 +75,7 @@ static TSConfigCacheEntry *lastUsedConfig = NULL;
  */
 char	   *TSCurrentConfig = NULL;
 
+static MemoryContext TextSearchCacheContext = NULL;
 static Oid	TSCurrentConfigCache = InvalidOid;
 
 
@@ -106,6 +107,20 @@ InvalidateTSCacheCallBack(Datum arg, int cacheid, uint32 hashvalue)
 		TSCurrentConfigCache = InvalidOid;
 }
 
+static void
+CreateTextSearchCacheContext(void)
+{
+	if (!CacheMemoryContext)
+		CreateCacheMemoryContext();
+
+	/*
+	 * TextSearch cache does not have a separate memory context,
+	 * CacheMemoryContext is used.
+	 */
+	if (!TextSearchCacheContext)
+		TextSearchCacheContext = CacheMemoryContext;
+}
+
 /*
  * Fetch parser cache entry
  */
@@ -127,9 +142,9 @@ lookup_ts_parser_cache(Oid prsId)
 		CacheRegisterSyscacheCallback(TSPARSEROID, InvalidateTSCacheCallBack,
 									  PointerGetDatum(TSParserCacheHash));
 
-		/* Also make sure CacheMemoryContext exists */
-		if (!CacheMemoryContext)
-			CreateCacheMemoryContext();
+		/* Also make sure TextSearchCacheContext exists */
+		if (!TextSearchCacheContext)
+			CreateTextSearchCacheContext();
 	}
 
 	/* Check single-entry cache */
@@ -186,12 +201,12 @@ lookup_ts_parser_cache(Oid prsId)
 
 		ReleaseSysCache(tp);
 
-		fmgr_info_cxt(entry->startOid, &entry->prsstart, CacheMemoryContext);
-		fmgr_info_cxt(entry->tokenOid, &entry->prstoken, CacheMemoryContext);
-		fmgr_info_cxt(entry->endOid, &entry->prsend, CacheMemoryContext);
+		fmgr_info_cxt(entry->startOid, &entry->prsstart, TextSearchCacheContext);
+		fmgr_info_cxt(entry->tokenOid, &entry->prstoken, TextSearchCacheContext);
+		fmgr_info_cxt(entry->endOid, &entry->prsend, TextSearchCacheContext);
 		if (OidIsValid(entry->headlineOid))
 			fmgr_info_cxt(entry->headlineOid, &entry->prsheadline,
-						  CacheMemoryContext);
+						  TextSearchCacheContext);
 
 		entry->isvalid = true;
 	}
@@ -224,9 +239,9 @@ lookup_ts_dictionary_cache(Oid dictId)
 		CacheRegisterSyscacheCallback(TSTEMPLATEOID, InvalidateTSCacheCallBack,
 									  PointerGetDatum(TSDictionaryCacheHash));
 
-		/* Also make sure CacheMemoryContext exists */
-		if (!CacheMemoryContext)
-			CreateCacheMemoryContext();
+		/* Also make sure TextSearchCacheContext exists */
+		if (!TextSearchCacheContext)
+			CreateTextSearchCacheContext();
 	}
 
 	/* Check single-entry cache */
@@ -291,7 +306,7 @@ lookup_ts_dictionary_cache(Oid dictId)
 			Assert(!found);		/* it wasn't there a moment ago */
 
 			/* Create private memory context the first time through */
-			saveCtx = AllocSetContextCreate(CacheMemoryContext,
+			saveCtx = AllocSetContextCreate(TextSearchCacheContext,
 											"TS dictionary",
 											ALLOCSET_SMALL_SIZES);
 			MemoryContextCopyAndSetIdentifier(saveCtx, NameStr(dict->dictname));
@@ -373,9 +388,9 @@ init_ts_config_cache(void)
 	CacheRegisterSyscacheCallback(TSCONFIGMAP, InvalidateTSCacheCallBack,
 								  PointerGetDatum(TSConfigCacheHash));
 
-	/* Also make sure CacheMemoryContext exists */
-	if (!CacheMemoryContext)
-		CreateCacheMemoryContext();
+	/* Also make sure TextSearchCacheContext exists */
+	if (!TextSearchCacheContext)
+		CreateTextSearchCacheContext();
 }
 
 /*
@@ -498,7 +513,7 @@ lookup_ts_config_cache(Oid cfgId)
 				{
 					maplists[maxtokentype].len = ndicts;
 					maplists[maxtokentype].dictIds = (Oid *)
-						MemoryContextAlloc(CacheMemoryContext,
+						MemoryContextAlloc(TextSearchCacheContext,
 										   sizeof(Oid) * ndicts);
 					memcpy(maplists[maxtokentype].dictIds, mapdicts,
 						   sizeof(Oid) * ndicts);
@@ -525,14 +540,14 @@ lookup_ts_config_cache(Oid cfgId)
 			/* save the last token type's dictionaries */
 			maplists[maxtokentype].len = ndicts;
 			maplists[maxtokentype].dictIds = (Oid *)
-				MemoryContextAlloc(CacheMemoryContext,
+				MemoryContextAlloc(TextSearchCacheContext,
 								   sizeof(Oid) * ndicts);
 			memcpy(maplists[maxtokentype].dictIds, mapdicts,
 				   sizeof(Oid) * ndicts);
 			/* and save the overall map */
 			entry->lenmap = maxtokentype + 1;
 			entry->map = (ListDictionary *)
-				MemoryContextAlloc(CacheMemoryContext,
+				MemoryContextAlloc(TextSearchCacheContext,
 								   sizeof(ListDictionary) * entry->lenmap);
 			memcpy(entry->map, maplists,
 				   sizeof(ListDictionary) * entry->lenmap);
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 5a3b3788d0..9c0bed45cb 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -78,6 +78,8 @@
 /* The main type cache hashtable searched by lookup_type_cache */
 static HTAB *TypeCacheHash = NULL;
 
+static MemoryContext TypCacheContext = NULL;
+
 /*
  * The mapping of relation's OID to the corresponding composite type OID.
  * We're keeping the map entry when the corresponding typentry has something
@@ -362,6 +364,20 @@ type_cache_syshash(const void *key, Size keysize)
 	return GetSysCacheHashValue1(TYPEOID, ObjectIdGetDatum(*(const Oid *) key));
 }
 
+static void
+CreateTypCacheContext(void)
+{
+	if (!CacheMemoryContext)
+		CreateCacheMemoryContext();
+
+	/*
+	 * TypCache does not have a separate memory context, CacheMemoryContext is
+	 * used.
+	 */
+	if (!TypCacheContext)
+		TypCacheContext = CacheMemoryContext;
+}
+
 /*
  * lookup_type_cache
  *
@@ -421,16 +437,16 @@ lookup_type_cache(Oid type_id, int flags)
 		CacheRegisterSyscacheCallback(CLAOID, TypeCacheOpcCallback, (Datum) 0);
 		CacheRegisterSyscacheCallback(CONSTROID, TypeCacheConstrCallback, (Datum) 0);
 
-		/* Also make sure CacheMemoryContext exists */
-		if (!CacheMemoryContext)
-			CreateCacheMemoryContext();
+		/* Also make sure TypCacheContext exists */
+		if (!TypCacheContext)
+			CreateTypCacheContext();
 
 		/*
 		 * reserve enough in_progress_list slots for many cases
 		 */
 		allocsize = 4;
 		in_progress_list =
-			MemoryContextAlloc(CacheMemoryContext,
+			MemoryContextAlloc(TypCacheContext,
 							   allocsize * sizeof(*in_progress_list));
 		in_progress_list_maxlen = allocsize;
 	}
@@ -854,7 +870,7 @@ lookup_type_cache(Oid type_id, int flags)
 	/*
 	 * Set up fmgr lookup info as requested
 	 *
-	 * Note: we tell fmgr the finfo structures live in CacheMemoryContext,
+	 * Note: we tell fmgr the finfo structures live in TypCacheContext,
 	 * which is not quite right (they're really in the hash table's private
 	 * memory context) but this will do for our purposes.
 	 *
@@ -872,21 +888,21 @@ lookup_type_cache(Oid type_id, int flags)
 		eq_opr_func = get_opcode(typentry->eq_opr);
 		if (eq_opr_func != InvalidOid)
 			fmgr_info_cxt(eq_opr_func, &typentry->eq_opr_finfo,
-						  CacheMemoryContext);
+						  TypCacheContext);
 	}
 	if ((flags & TYPECACHE_CMP_PROC_FINFO) &&
 		typentry->cmp_proc_finfo.fn_oid == InvalidOid &&
 		typentry->cmp_proc != InvalidOid)
 	{
 		fmgr_info_cxt(typentry->cmp_proc, &typentry->cmp_proc_finfo,
-					  CacheMemoryContext);
+					  TypCacheContext);
 	}
 	if ((flags & TYPECACHE_HASH_PROC_FINFO) &&
 		typentry->hash_proc_finfo.fn_oid == InvalidOid &&
 		typentry->hash_proc != InvalidOid)
 	{
 		fmgr_info_cxt(typentry->hash_proc, &typentry->hash_proc_finfo,
-					  CacheMemoryContext);
+					  TypCacheContext);
 	}
 	if ((flags & TYPECACHE_HASH_EXTENDED_PROC_FINFO) &&
 		typentry->hash_extended_proc_finfo.fn_oid == InvalidOid &&
@@ -894,7 +910,7 @@ lookup_type_cache(Oid type_id, int flags)
 	{
 		fmgr_info_cxt(typentry->hash_extended_proc,
 					  &typentry->hash_extended_proc_finfo,
-					  CacheMemoryContext);
+					  TypCacheContext);
 	}
 
 	/*
@@ -1039,13 +1055,13 @@ load_rangetype_info(TypeCacheEntry *typentry)
 
 	/* set up cached fmgrinfo structs */
 	fmgr_info_cxt(cmpFnOid, &typentry->rng_cmp_proc_finfo,
-				  CacheMemoryContext);
+				  TypCacheContext);
 	if (OidIsValid(canonicalOid))
 		fmgr_info_cxt(canonicalOid, &typentry->rng_canonical_finfo,
-					  CacheMemoryContext);
+					  TypCacheContext);
 	if (OidIsValid(subdiffOid))
 		fmgr_info_cxt(subdiffOid, &typentry->rng_subdiff_finfo,
-					  CacheMemoryContext);
+					  TypCacheContext);
 
 	/* Lastly, set up link to the element type --- this marks data valid */
 	typentry->rngelemtype = lookup_type_cache(subtypeOid, 0);
@@ -1074,7 +1090,7 @@ load_multirangetype_info(TypeCacheEntry *typentry)
  * Note: we assume we're called in a relatively short-lived context, so it's
  * okay to leak data into the current context while scanning pg_constraint.
  * We build the new DomainConstraintCache data in a context underneath
- * CurrentMemoryContext, and reparent it under CacheMemoryContext when
+ * CurrentMemoryContext, and reparent it under TypCacheContext when
  * complete.
  */
 static void
@@ -1296,12 +1312,12 @@ load_domaintype_info(TypeCacheEntry *typentry)
 	}
 
 	/*
-	 * If we made a constraint object, move it into CacheMemoryContext and
+	 * If we made a constraint object, move it into TypCacheContext and
 	 * attach it to the typcache entry.
 	 */
 	if (dcc)
 	{
-		MemoryContextSetParent(dcc->dccContext, CacheMemoryContext);
+		MemoryContextSetParent(dcc->dccContext, TypCacheContext);
 		typentry->domainData = dcc;
 		dcc->dccRefCount++;		/* count the typcache's reference */
 	}
@@ -1799,7 +1815,7 @@ ensure_record_cache_typmod_slot_exists(int32 typmod)
 	if (RecordCacheArray == NULL)
 	{
 		RecordCacheArray = (RecordCacheArrayEntry *)
-			MemoryContextAllocZero(CacheMemoryContext,
+			MemoryContextAllocZero(TypCacheContext,
 								   64 * sizeof(RecordCacheArrayEntry));
 		RecordCacheArrayLen = 64;
 	}
@@ -2059,9 +2075,9 @@ assign_record_type_typmod(TupleDesc tupDesc)
 									  &ctl,
 									  HASH_ELEM | HASH_FUNCTION | HASH_COMPARE);
 
-		/* Also make sure CacheMemoryContext exists */
-		if (!CacheMemoryContext)
-			CreateCacheMemoryContext();
+		/* Also make sure TypCacheContext exists */
+		if (!TypCacheContext)
+			CreateTypCacheContext();
 	}
 
 	/*
@@ -2079,7 +2095,7 @@ assign_record_type_typmod(TupleDesc tupDesc)
 	}
 
 	/* Not present, so need to manufacture an entry */
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(TypCacheContext);
 
 	/* Look in the SharedRecordTypmodRegistry, if attached */
 	entDesc = find_or_make_matching_shared_tupledesc(tupDesc);
@@ -2746,8 +2762,8 @@ load_enum_cache_data(TypeCacheEntry *tcache)
 	/*
 	 * Read all the information for members of the enum type.  We collect the
 	 * info in working memory in the caller's context, and then transfer it to
-	 * permanent memory in CacheMemoryContext.  This minimizes the risk of
-	 * leaking memory from CacheMemoryContext in the event of an error partway
+	 * permanent memory in TypCacheContext.  This minimizes the risk of
+	 * leaking memory from TypCacheContext in the event of an error partway
 	 * through.
 	 */
 	maxitems = 64;
@@ -2851,8 +2867,8 @@ load_enum_cache_data(TypeCacheEntry *tcache)
 			break;
 	}
 
-	/* OK, copy the data into CacheMemoryContext */
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	/* OK, copy the data into TypCacheContext */
+	oldcxt = MemoryContextSwitchTo(TypCacheContext);
 	enumdata = (TypeCacheEnumData *)
 		palloc(offsetof(TypeCacheEnumData, enum_values) +
 			   numitems * sizeof(EnumItem));
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index a7c55db339..07d8ec4cd8 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -95,6 +95,7 @@ extern int	errtableconstraint(Relation rel, const char *conname);
 /*
  * Routines for backend startup
  */
+extern void CreateRelCacheContext(void);
 extern void RelationCacheInitialize(void);
 extern void RelationCacheInitializePhase2(void);
 extern void RelationCacheInitializePhase3(void);
@@ -152,4 +153,6 @@ extern PGDLLIMPORT bool criticalRelcachesBuilt;
 /* should be used only by relcache.c and postinit.c */
 extern PGDLLIMPORT bool criticalSharedRelcachesBuilt;
 
+extern PGDLLIMPORT MemoryContext RelCacheContext;
+
 #endif							/* RELCACHE_H */
-- 
2.34.1

