diff --git a/src/backend/utils/mmgr/Makefile b/src/backend/utils/mmgr/Makefile
index 3b4cfdb..3d1e28f 100644
--- a/src/backend/utils/mmgr/Makefile
+++ b/src/backend/utils/mmgr/Makefile
@@ -20,6 +20,9 @@ OBJS = \
 	mcxt.o \
 	memdebug.o \
 	portalmem.o \
-	slab.o
+	slab.o \
+	shm_context.o \
+	shm_retail.o \
+	shm_zone.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index 9e24fec..270570e 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -48,6 +48,9 @@ MemoryContext CacheMemoryContext = NULL;
 MemoryContext MessageContext = NULL;
 MemoryContext TopTransactionContext = NULL;
 MemoryContext CurTransactionContext = NULL;
+MemoryContext GlobalCacheContext = NULL;
+MemoryContext GlobalCatCacheContext = NULL;
+MemoryContext GlobalRelCacheContext = NULL;
 
 /* This is a transient link to the active portal's memory context: */
 MemoryContext PortalContext = NULL;
@@ -67,6 +70,10 @@ static void MemoryContextStatsPrint(MemoryContext context, void *passthru,
 #define AssertNotInCriticalSection(context) \
 	Assert(CritSectionCount == 0 || (context)->allowInCritSection)
 
+/* if the context is located in local memory or shared memory  */
+#define MemoryContextIsLocal(context) \
+	((context->methodsID) == InvalidShmContextMethodsID)
+
 /*****************************************************************************
  *	  EXPORTED ROUTINES														 *
  *****************************************************************************/
@@ -171,7 +178,11 @@ MemoryContextResetOnly(MemoryContext context)
 		 * for now we assume the programmer got it right.
 		 */
 
-		context->methods->reset(context);
+		if (MemoryContextIsLocal(context))
+			context->methods->reset(context);
+		else
+			shmcxt_methods[context->methodsID].reset(context);
+
 		context->isReset = true;
 		VALGRIND_DESTROY_MEMPOOL(context);
 		VALGRIND_CREATE_MEMPOOL(context, 0, false);
@@ -242,7 +253,10 @@ MemoryContextDelete(MemoryContext context)
 	 */
 	context->ident = NULL;
 
-	context->methods->delete_context(context);
+	if (MemoryContextIsLocal(context))
+		context->methods->delete_context(context);
+	else
+		shmcxt_methods[context->methodsID].delete_context(context);
 
 	VALGRIND_DESTROY_MEMPOOL(context);
 }
@@ -397,6 +411,61 @@ MemoryContextSetParent(MemoryContext context, MemoryContext new_parent)
 }
 
 /*
+ * MemoryContextClone
+ *  	Create a new MemoryContext of the same type as template context
+ * 		and set its parent to specified one.
+ */
+MemoryContext
+MemoryContextClone(MemoryContext template, MemoryContext parent)
+{
+	MemoryContext context;
+
+	AssertArg(MemoryContextIsValid(template));
+	AssertArg(MemoryContextIsValid(parent));
+
+	switch (nodeTag(template))
+	{
+		case T_AllocSetContext:
+		case T_SlabContext:
+		case T_GenerationContext:
+			elog(FATAL, " MemoryContextClone doesn't support cloning"
+				 "memory context (%s)", template->name);
+			break;
+		case T_ShmRetailContext:
+			context = ShmRetailContextCreateLocal(parent,
+												  "clone",
+												  template);
+			break;
+		case T_ShmZoneContext:
+			{
+				MemoryContext old_context;
+
+				/* cloned context is created in shared memory */
+				old_context = MemoryContextSwitchTo(template);
+				context = ShmZoneContextCreateClone(parent,
+													"clone",template);
+				MemoryContextSwitchTo(old_context);
+				break;
+			}
+		default:
+			elog(FATAL, "unrecognized node type: %d",
+				 (int) nodeTag(template));
+	}
+	return context;
+}
+
+/*
+ * MemoryContextSetShmContextMethodsID
+ * 		Set shmContextMethodId for shared memory context.
+ */
+void
+MemoryContextSetShmContextMethodsID(MemoryContext node,
+									ShmContextMethodsID id)
+{
+	node->methodsID = id;
+}
+
+/*
  * MemoryContextAllowInCriticalSection
  *		Allow/disallow allocations in this memory context within a critical
  *		section.
@@ -428,7 +497,12 @@ GetMemoryChunkSpace(void *pointer)
 {
 	MemoryContext context = GetMemoryChunkContext(pointer);
 
-	return context->methods->get_chunk_space(context, pointer);
+	if (MemoryContextIsLocal(context))
+		return context->methods->get_chunk_space(context, pointer);
+	else
+		return shmcxt_methods[context->methodsID].get_chunk_space(context,
+																  pointer);
+
 }
 
 /*
@@ -459,7 +533,11 @@ MemoryContextIsEmpty(MemoryContext context)
 	if (context->firstchild != NULL)
 		return false;
 	/* Otherwise use the type-specific inquiry */
-	return context->methods->is_empty(context);
+	if (MemoryContextIsLocal(context))
+		return context->methods->is_empty(context);
+	else
+		return shmcxt_methods[context->methodsID].is_empty(context);
+
 }
 
 /*
@@ -541,10 +619,18 @@ MemoryContextStatsInternal(MemoryContext context, int level,
 	AssertArg(MemoryContextIsValid(context));
 
 	/* Examine the context itself */
-	context->methods->stats(context,
-							print ? MemoryContextStatsPrint : NULL,
-							(void *) &level,
-							totals);
+	if (MemoryContextIsLocal(context))
+		context->methods->stats(context,
+								print ? MemoryContextStatsPrint : NULL,
+								(void *) &level,
+								totals);
+	else
+		shmcxt_methods[context->methodsID]
+			.stats(context,
+					print ? MemoryContextStatsPrint : NULL,
+					(void *) &level,
+					totals);
+
 
 	/*
 	 * Examine children.  If there are more than max_children of them, we do
@@ -670,7 +756,11 @@ MemoryContextCheck(MemoryContext context)
 
 	AssertArg(MemoryContextIsValid(context));
 
-	context->methods->check(context);
+	if (MemoryContextIsLocal(context))
+		context->methods->check(context);
+	else
+		shmcxt_methods[context->methodsID].check(context);
+
 	for (child = context->firstchild; child != NULL; child = child->nextchild)
 		MemoryContextCheck(child);
 }
@@ -765,6 +855,7 @@ MemoryContextCreate(MemoryContext node,
 	node->name = name;
 	node->ident = NULL;
 	node->reset_cbs = NULL;
+	node->methodsID = InvalidShmContextMethodsID;
 
 	/* OK to link node into context tree */
 	if (parent)
@@ -805,7 +896,11 @@ MemoryContextAlloc(MemoryContext context, Size size)
 
 	context->isReset = false;
 
-	ret = context->methods->alloc(context, size);
+	if (MemoryContextIsLocal(context))
+		ret = context->methods->alloc(context, size);
+	else
+		ret = shmcxt_methods[context->methodsID].alloc(context, size);
+
 	if (unlikely(ret == NULL))
 	{
 		MemoryContextStats(TopMemoryContext);
@@ -848,7 +943,11 @@ MemoryContextAllocZero(MemoryContext context, Size size)
 
 	context->isReset = false;
 
-	ret = context->methods->alloc(context, size);
+	if (MemoryContextIsLocal(context))
+		ret = context->methods->alloc(context, size);
+	else
+		ret = shmcxt_methods[context->methodsID].alloc(context, size);
+
 	if (unlikely(ret == NULL))
 	{
 		MemoryContextStats(TopMemoryContext);
@@ -886,7 +985,11 @@ MemoryContextAllocZeroAligned(MemoryContext context, Size size)
 
 	context->isReset = false;
 
-	ret = context->methods->alloc(context, size);
+	if (MemoryContextIsLocal(context))
+		ret = context->methods->alloc(context, size);
+	else
+		ret = shmcxt_methods[context->methodsID].alloc(context, size);
+
 	if (unlikely(ret == NULL))
 	{
 		MemoryContextStats(TopMemoryContext);
@@ -922,7 +1025,11 @@ MemoryContextAllocExtended(MemoryContext context, Size size, int flags)
 
 	context->isReset = false;
 
-	ret = context->methods->alloc(context, size);
+	if (MemoryContextIsLocal(context))
+		ret = context->methods->alloc(context, size);
+	else
+		ret = shmcxt_methods[context->methodsID].alloc(context, size);
+
 	if (unlikely(ret == NULL))
 	{
 		if ((flags & MCXT_ALLOC_NO_OOM) == 0)
@@ -960,7 +1067,11 @@ palloc(Size size)
 
 	context->isReset = false;
 
-	ret = context->methods->alloc(context, size);
+	if (MemoryContextIsLocal(context))
+		ret = context->methods->alloc(context, size);
+	else
+		ret = shmcxt_methods[context->methodsID].alloc(context, size);
+
 	if (unlikely(ret == NULL))
 	{
 		MemoryContextStats(TopMemoryContext);
@@ -991,7 +1102,11 @@ palloc0(Size size)
 
 	context->isReset = false;
 
-	ret = context->methods->alloc(context, size);
+	if (MemoryContextIsLocal(context))
+		ret = context->methods->alloc(context, size);
+	else
+		ret = shmcxt_methods[context->methodsID].alloc(context, size);
+
 	if (unlikely(ret == NULL))
 	{
 		MemoryContextStats(TopMemoryContext);
@@ -1025,7 +1140,11 @@ palloc_extended(Size size, int flags)
 
 	context->isReset = false;
 
-	ret = context->methods->alloc(context, size);
+	if (MemoryContextIsLocal(context))
+		ret = context->methods->alloc(context, size);
+	else
+		ret = shmcxt_methods[context->methodsID].alloc(context, size);
+
 	if (unlikely(ret == NULL))
 	{
 		if ((flags & MCXT_ALLOC_NO_OOM) == 0)
@@ -1057,7 +1176,11 @@ pfree(void *pointer)
 {
 	MemoryContext context = GetMemoryChunkContext(pointer);
 
-	context->methods->free_p(context, pointer);
+	if (MemoryContextIsLocal(context))
+		context->methods->free_p(context, pointer);
+	else
+		shmcxt_methods[context->methodsID].free_p(context, pointer);
+
 	VALGRIND_MEMPOOL_FREE(context, pointer);
 }
 
@@ -1079,7 +1202,12 @@ repalloc(void *pointer, Size size)
 	/* isReset must be false already */
 	Assert(!context->isReset);
 
-	ret = context->methods->realloc(context, pointer, size);
+	if (MemoryContextIsLocal(context))
+		ret = context->methods->realloc(context, pointer, size);
+	else
+		ret = shmcxt_methods[context->methodsID]
+			.realloc(context, pointer, size);
+
 	if (unlikely(ret == NULL))
 	{
 		MemoryContextStats(TopMemoryContext);
@@ -1114,7 +1242,11 @@ MemoryContextAllocHuge(MemoryContext context, Size size)
 
 	context->isReset = false;
 
-	ret = context->methods->alloc(context, size);
+	if (MemoryContextIsLocal(context))
+		ret = context->methods->alloc(context, size);
+	else
+		ret = shmcxt_methods[context->methodsID].alloc(context, size);
+
 	if (unlikely(ret == NULL))
 	{
 		MemoryContextStats(TopMemoryContext);
@@ -1149,7 +1281,12 @@ repalloc_huge(void *pointer, Size size)
 	/* isReset must be false already */
 	Assert(!context->isReset);
 
-	ret = context->methods->realloc(context, pointer, size);
+	if (MemoryContextIsLocal(context))
+		ret = context->methods->realloc(context, pointer, size);
+	else
+		ret = shmcxt_methods[context->methodsID]
+			.realloc(context, pointer, size);
+
 	if (unlikely(ret == NULL))
 	{
 		MemoryContextStats(TopMemoryContext);
diff --git a/src/backend/utils/mmgr/shm_context.c b/src/backend/utils/mmgr/shm_context.c
new file mode 100644
index 0000000..a5d220d
--- /dev/null
+++ b/src/backend/utils/mmgr/shm_context.c
@@ -0,0 +1,149 @@
+/*-------------------------------------------------------------------------
+ *
+ * shm_context.c
+ *	  General utilities for ShmRetailContext and ShmZoneContext.
+ *
+ * Both ShmRetailContext and ShmZoneContext is a MemoryContext implementation
+ * designed for cases where you want to allocate and free objects in the
+ * shared memory by MemoryContext API. This file provides utilities for
+ * both implementations.
+ *
+ * Portions Copyright (c) 2017-2020, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/mmgr/shm_context.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "storage/shmem.h"
+#include "utils/dsa.h"
+#include "utils/memdebug.h"
+#include "utils/memutils.h"
+
+/*
+ * Global meta cache area. Note that dsa_area is allocated in backend and
+ * can not be shared among backends.
+ */
+static void *gmc_raw_area;
+static dsa_area *gmc_dsa_area;
+
+/*
+ * Genreral type for Shared MemoryContext.
+ * See also ShmRetailContext and ShmZoneContext
+ */
+typedef struct ShmContext
+{
+	MemoryContextData header;	/* Standard memory-context fields */
+	/* ShmRetailContext parameters */
+	LWLock *child_lock;		   	/* protect child graph path */
+} ShmContext;
+
+/*
+ * Create GMC area and map it with GlobalCacheContext backed by DSA
+ */
+void
+GlobalCacheContextShmemInit(Size size)
+{
+	MemoryContext	old_context;
+	bool	found;
+
+	/* Life span of dsa_area is same as this process */
+	old_context = MemoryContextSwitchTo(TopMemoryContext);
+
+	/* Allocate, or look up, a chunk of raw fixed-address shared memory. */
+	gmc_raw_area = ShmemInitStruct("global_meta_cache", size, &found);
+
+	if (!found)
+	{
+		/*
+		 * Create a new DSA area, and clamp its size so it can't make any
+		 * segments outside the provided space.
+		 */
+		gmc_dsa_area = dsa_create_in_place(gmc_raw_area, size, 0, NULL);
+
+		dsa_set_size_limit(gmc_dsa_area, size);
+
+		/* To prevent the area from being released */
+		dsa_pin(gmc_dsa_area);
+	}
+	else
+	{
+		/* Attach to an existing area and get dsa_area */
+		gmc_dsa_area = dsa_attach_in_place(gmc_raw_area, NULL);
+	}
+
+	/*
+	 * Set dsa_area of GMC so that other shared MemoryContext can use it
+	 */
+	ShmRetailContextSetDSAarea(gmc_dsa_area);
+	ShmZoneContextSetDSAarea(gmc_dsa_area);
+
+	/* Create a shared memory context */
+	GlobalCacheContext
+		= ShmRetailContextCreateGlobal(NULL,
+									   "global_cache_context",
+									   gmc_raw_area);
+
+	MemoryContextSwitchTo(old_context);
+}
+
+void
+GlobalCatCacheContextShmemInit(void)
+{
+	/* Create or attach to shared memory context */
+	GlobalCatCacheContext
+		= ShmRetailContextCreateGlobal(GlobalCacheContext,
+									   "global_catcache_context",
+									   gmc_raw_area);
+}
+
+void
+GlobalRelCacheContextShmemInit(void)
+{
+	/* Create or attach to shared memory context */
+	GlobalRelCacheContext
+		= ShmZoneContextCreateOrigin(GlobalCacheContext,
+									 "global_relcache_context",
+									 gmc_raw_area);
+}
+
+/*
+ * Return size of GlobalCacheContext
+ */
+Size
+GlobalCacheContextSize(void)
+{
+	return ShmRetailContextSize();
+}
+
+/*
+ * Return size of GlobalCatCacheContext
+ */
+Size
+GlobalCatCacheContextSize(void)
+{
+	return ShmRetailContextSize();
+}
+
+/*
+ * Return size of GlobalRelCacheContext
+ */
+Size
+GlobalRelCacheContextSize(void)
+{
+	return ShmZoneContextSize();
+}
+
+/*
+ * General function for get context child path lock
+ */
+LWLock *
+ShmContextGetLock(MemoryContext context)
+{
+	Assert(MemoryContextIsShared(context));
+
+	return ((ShmContext *)context)->child_lock;
+}
diff --git a/src/backend/utils/mmgr/shm_retail.c b/src/backend/utils/mmgr/shm_retail.c
new file mode 100644
index 0000000..3d513ef
--- /dev/null
+++ b/src/backend/utils/mmgr/shm_retail.c
@@ -0,0 +1,583 @@
+/*-------------------------------------------------------------------------
+ *
+ * shm_retail.c
+ *	  ShmRetailContext allocator definitions.
+ *
+ * ShmRetailContext is a MemoryContext implementation designed for cases where
+ * you want to allocate and free objects in the shared memory by MemoryContext
+ * API (palloc/pfree). The main use case is global system catalog cache.
+ *
+ * Portions Copyright (c) 2017-2020, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/mmgr/shm_retail.c
+ *
+ *
+ * NOTE:
+ *
+ * ShmRetailContext allows allocation in the shared memory via palloc().
+ * This is intended to migrate locally allocated objects into shared memory.
+ * These objects could be syscache or somthing else. They usually
+ * allocate memory in local heap by palloc(). To take advantage of exisiting
+ * code, ShmRetailContext uses dsa_allocate()/dsa_free() as palloc()/pfree().
+ * However, dsa_allocate() returns dsa_pointer while palloc() returns native
+ * pointer. And also an object may be a graph structure with pointers.
+ * It needs to remember either native pointer or dsa_pointer.
+ *
+ * So allow the creation of DSA areas inside the traditional fixed memory
+ * segment (instead of DSM), in a fixed-sized space reserved by the postmaster.
+ * In this case, dsa_pointer is directly castable to a raw pointer, which is
+ * common to every process. This fits to regular MemoryContext interface. But
+ * note that the total size is fixed at start up time.
+ *
+ * Now we can put objects into shared memory via palloc(). But without
+ * some garbage collection mechanism, memory leaks will be cluster-wide
+ * and cluster-life-time. Leaked object won't go away even if one backend
+ * exits.
+ *
+ * To address this issue, ShmRetailContext has two types of context: "local" and
+ * "global" one. Local context itself is located in local memory but chunks
+ * are allocated in shared memory. These chunks are pointed by chunk-list via
+ * dsa_pointer in order to free them all at once at transaction rollback.
+ * Once chunks are registerd to some index strucure in shared memory so that
+ * memory leak won't happen, you can move chunks to global ShmRetailContext,
+ * which is located in shared memory.
+ *
+ * Example of usage is found in:
+ *	 src/test/modules/test_shm_retail
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "lib/ilist.h"
+#include "storage/shmem.h"
+#include "utils/dsa.h"
+#include "utils/memdebug.h"
+#include "utils/memutils.h"
+
+
+/*
+ * An arbitrary number for how many chunks is pointed by one chunk list
+ * Use case like global system catalog cache calls palloc() in couple of
+ * times in one memory context. So 16 would be enough.
+ */
+#define NUM_CHUNKS 32
+
+typedef struct chunk_list chunk_list;
+
+/*
+ * ShmRetailContext is a specialized memory context supporting dsa area created
+ * above Postmaster-initialized shared memory
+ */
+typedef struct ShmRetailContext
+{
+	MemoryContextData header;	/* Standard memory-context fields */
+	/* ShmRetailContext parameters */
+	LWLock *child_lock;		   	/* protect child graph path */
+	void	 *base;				/* raw address of Postmaster-initialized
+								   shared memory */
+	chunk_list *chunk_list; 	/* array of pointers to chunks. If
+								   ShmRetailContext is permanent, NULL */
+} ShmRetailContext;
+
+/*
+ * chunk_list
+ *		keeping dsa_pointer to chunks to free them at rollback
+ *
+ * Local ShmRetailContext have this list while permanent one does not.
+ * If list becomes full, next list is pushed to head.
+ */
+struct chunk_list
+{
+	chunk_list *next; 				/* single linked list */
+	int tail;						/* index of array to be allocated */
+	dsa_pointer chunks[NUM_CHUNKS]; /* relative address of chunks */
+};
+
+
+#define isLocalShmRetailContext(shm_context) ((shm_context)->chunk_list != NULL)
+#define isListFull(list) ((list)->tail == NUM_CHUNKS)
+
+ /* Helper function for ShmRetailContext API */
+static inline MemoryContext
+ShmRetailContextCreateInternal(MemoryContext parent,
+							   const char *name,
+							   void *base,
+							   bool isLocal);
+static void push_chunk_list(ShmRetailContext *shmContext);
+
+/*
+ * These functions implement the MemoryContext API for ShmRetailContext.
+ */
+static void *ShmRetailContextAlloc(MemoryContext context, Size size);
+static void ShmRetailContextFree(MemoryContext context, void *pointer);
+static void *ShmRetailContextRealloc(MemoryContext context, void *pointer, Size size);
+static void ShmRetailContextReset(MemoryContext context);
+static void ShmRetailContextDelete(MemoryContext context);
+static Size ShmRetailContextGetChunkSpace(MemoryContext context, void *pointer);
+static bool ShmRetailContextIsEmpty(MemoryContext context);
+static void ShmRetailContextStats(MemoryContext context,
+					  MemoryStatsPrintFunc printfunc, void *passthru,
+					  MemoryContextCounters *totals);
+#ifdef MEMORY_CONTEXT_CHECKING
+static void ShmRetailContextCheck(MemoryContext context);
+#endif
+
+/*
+ * This is the virtual function table for ShmRetailContext contexts.
+ */
+static const MemoryContextMethods ShmRetailContextMethods = {
+	ShmRetailContextAlloc,
+	ShmRetailContextFree,
+	ShmRetailContextRealloc,
+	ShmRetailContextReset,
+	ShmRetailContextDelete,
+	ShmRetailContextGetChunkSpace,
+	ShmRetailContextIsEmpty,
+	ShmRetailContextStats
+#ifdef MEMORY_CONTEXT_CHECKING
+	,ShmRetailContextCheck
+#endif
+};
+
+static dsa_area *shm_retail_area = NULL;
+
+/* Set and Get DSA area of global meta cache */
+void
+ShmRetailContextSetDSAarea(dsa_area *area)
+{
+	if (!shm_retail_area)
+		shm_retail_area = area;
+}
+
+dsa_area *
+ShmRetailContextGetDSAarea(void)
+{
+	return shm_retail_area;
+}
+
+/*
+ * ShmRetailContextCreateGlobal
+ *		Create a new permanent ShmRetailContext context.
+ *
+ * parent: parent context, or NULL if top-level context
+ * name: name of context (must be statically allocated)
+ * area: dsa_area created in place of Postmaster-initialized shared memory
+ * base: address of Postmaster-initialized shared memory
+ *
+ * This context itself is allocated on shared memory.
+ */
+MemoryContext
+ShmRetailContextCreateGlobal(MemoryContext parent,
+							 const char *name, void *base)
+{
+	return ShmRetailContextCreateInternal(parent, name, base, false);
+}
+
+/*
+ * CreateTempShmRetailContext
+ *		Create local ShmRetailContext in local heap by ShmRetailContextCreate
+ *
+ * parent: parent context, or NULL if top-level context
+ * name: name of context (must be statically allocated)
+ * global_context: permanent shared MemoryContext
+ *
+ * Temp context inherits dsa_area and base address of permanent ShmRetailContext.
+ * This context itself is allocated on the parent context, which must not
+ * be permanent ShmRetailContext.
+ *
+ */
+MemoryContext
+ShmRetailContextCreateLocal(MemoryContext parent,
+							const char *name,
+							MemoryContext global_context)
+{
+	ShmRetailContext *shmContext;
+	AssertArg(MemoryContextIsValid(global_context));
+
+	shmContext = (ShmRetailContext *) global_context;
+
+	/* global_context should be permanent one */
+	Assert(!isLocalShmRetailContext(shmContext));
+
+	return ShmRetailContextCreateInternal(parent, name,
+										  shmContext->base, true);
+}
+
+
+/*
+ * ShmRetailContextCreateInternal
+ *		Work-horse for ShmRetailContextCreateGlobal/ShmRetailContextCreateLocal
+ *
+ * parent: parent context, or NULL if top-level context
+ * name: name of context (must be statically allocated)
+ * area: dsa_area created in place of Postmaster-initialized shared memory
+ * base: address of Postmaster-initialized shared memory
+ * isLocal: context is local?
+ *
+ */
+static inline MemoryContext
+ShmRetailContextCreateInternal(MemoryContext parent,
+							   const char *name,
+							   void *base,
+							   bool isLocal)
+{
+	ShmRetailContext *shmContext;
+	bool found;
+	LWLock *child_lock;
+	LWLock *context_lock;
+
+	/*
+	 * If context is temp, allocate it and its list in parent context.
+	 * If it is permanent, chunk_list is not used.
+	 */
+	if (isLocal)
+	{
+		MemoryContext old_context;
+
+		/* Assuming parent is local context */
+		old_context = MemoryContextSwitchTo(parent);
+
+		shmContext = palloc0(sizeof(ShmRetailContext));
+		shmContext->chunk_list = (chunk_list *)palloc0(sizeof(chunk_list));
+
+		MemoryContextSwitchTo(old_context);
+	}
+	else
+	{
+		shmContext = (ShmRetailContext *)
+			ShmemInitStruct(name, ShmRetailContextSize(), &found);
+		shmContext->chunk_list = NULL;
+
+		/*
+		 * Register MemoryContextMethods with methods table. This should be
+		 * called by each process in Windows or EXEC-BACKEND case.
+		 */
+		RegisterMemoryContextMethods(MEMORY_CONTEXT_SHARED_RETAIL,
+									 &ShmRetailContextMethods);
+
+		if (found)
+			return &shmContext->header;
+		else
+		{
+			child_lock = (LWLock *)
+				MAXALIGN(((char *)shmContext) + sizeof(ShmRetailContext));
+
+			LWLockInitialize(child_lock, LWTRANCHE_SHARED_MCXT);
+			shmContext->child_lock = child_lock;
+		}
+	}
+
+	/* Get parent context's child path lock */
+	if (!isLocal && parent != NULL)
+	{
+		context_lock = ShmContextGetLock(parent);
+		LWLockAcquire(context_lock, LW_EXCLUSIVE);
+	}
+
+	MemoryContextCreate(&shmContext->header,
+						T_ShmRetailContext,
+						&ShmRetailContextMethods,
+						parent,
+						name);
+
+	if (!isLocal && parent != NULL)
+		LWLockRelease(context_lock);
+
+	MemoryContextSetShmContextMethodsID(&shmContext->header,
+										MEMORY_CONTEXT_SHARED_RETAIL);
+
+	shmContext->base = base;
+
+	return &shmContext->header;
+}
+
+/*
+ * ShmRetailContextSize
+ *		Size of ShmRetailContext
+ * If you use ShmRetailContextCreateGlobal, this fucntion should be called
+ * to reserve the size of ShmRetailContext at postgres start-up time
+ */
+Size
+ShmRetailContextSize(void)
+{
+	return sizeof(ShmRetailContext) + MAXIMUM_ALIGNOF + sizeof(LWLock);
+}
+
+/*
+ * ShmRetailContextMoveChunk
+ *
+ * We don't want to leak memory in shared memory. Unlike local process,
+ * memory leak still exists even after local process is terminated.
+ * If error occurs in transaction, we free all dsa_allocated chunks linked
+ * from local ShmRetailContext. When you make sure that the memory leak does
+ * not happen, ShmRetailContextMoveChunk should be called.
+ *
+ */
+void
+ShmRetailContextMoveChunk(MemoryContext local_context, MemoryContext global_context)
+{
+	ShmRetailContext *localShmRetailContext = (ShmRetailContext *) local_context;
+	chunk_list *list = localShmRetailContext->chunk_list;
+	int idx;
+	char *chunk_backp;
+
+	/* silently return */
+	if (local_context == global_context)
+		return;
+
+	/* change backpointer to shared MemoryContext */
+	while (list && list->chunks)
+	{
+		chunk_list *next_list = list->next;
+
+		for (idx = 0; idx < list->tail; idx++)
+		{
+			chunk_backp = dsaptr_to_rawptr(list->chunks[idx], localShmRetailContext->base);
+			*(void **) chunk_backp = global_context;
+		}
+		/* initialize tail of list */
+
+		list->tail = 0;
+		list = next_list;
+	}
+}
+
+/*
+ * push_chunk_list
+ *		If chunk_list becomes full, add new list
+ */
+static void
+push_chunk_list(ShmRetailContext *shmContext)
+{
+	MemoryContext old_context;
+	chunk_list *new_list;
+
+	/* choose the same context as current list to make lifetime consistent */
+	old_context = MemoryContextSwitchTo(GetMemoryChunkContext(shmContext->chunk_list));
+
+	/* insert a new list into head position */
+	new_list = (chunk_list *) palloc0(sizeof(chunk_list));
+	new_list->next = shmContext->chunk_list;
+	shmContext->chunk_list = new_list;
+
+	MemoryContextSwitchTo(old_context);
+}
+
+
+/*
+ * ShmRetailContextReset
+ *		Free all the memory registered in chunk_list and list
+ *
+ * The chunks registered in chunk_list are dsa_freed.
+ * This does not affect permanent context.
+ *
+ */
+static void
+ShmRetailContextReset(MemoryContext context)
+{
+	int			idx;
+	chunk_list *list;
+	ShmRetailContext  *shmContext = (ShmRetailContext *) context;
+
+	Assert(shmContext);
+
+	/* We don't support reset if context is permanent */
+	if (!isLocalShmRetailContext(shmContext))
+		return;
+
+	list = shmContext->chunk_list;
+
+#ifdef MEMORY_CONTEXT_CHECKING
+	/* Check for corruption and leaks before freeing */
+	ShmRetailContextCheck(context);
+#endif
+
+	/* free all chunks and lists */
+	while (list && list->chunks)
+	{
+		chunk_list *next_list = list->next;
+
+		for (idx = 0; idx < list->tail; idx++)
+			dsa_free(shm_retail_area, list->chunks[idx]);
+
+		pfree(list);
+		list = next_list;
+	}
+}
+
+/*
+ * ShmRetailContextDelete
+ *		Free all the memory registered in chunk_list and context.
+ *		See ShmRetailContextReset.
+ */
+static void
+ShmRetailContextDelete(MemoryContext context)
+{
+	/* Reset to release all the chunk_lists */
+	ShmRetailContextReset(context);
+	/* And free the context header */
+	pfree(context);
+}
+
+/*
+ * ShmRetailContextAlloc
+ *		Returns native pointer to allocated memory
+ */
+static void *
+ShmRetailContextAlloc(MemoryContext context, Size size)
+{
+	ShmRetailContext *shmContext = (ShmRetailContext *) context;
+
+	char *chunk_backp;
+	chunk_list *list;
+
+	/* if shared context is specified, list is not used */
+	if (!isLocalShmRetailContext(shmContext))
+	{
+		dsa_pointer dp;
+
+		dp  = dsa_allocate(shm_retail_area,
+						   sizeof(void *) + size);
+
+		chunk_backp = dsaptr_to_rawptr(dp, shmContext->base);
+
+		*(void **) chunk_backp = context;
+
+		return chunk_backp + sizeof(void *);
+	}
+
+	/* if list is full, allocate a new list */
+	if (isListFull(shmContext->chunk_list))
+		push_chunk_list(shmContext);
+
+	/* Add space for the secret context pointer. */
+	list = shmContext->chunk_list;
+	list->chunks[list->tail] = dsa_allocate(shm_retail_area,
+											sizeof(void *) + size);
+
+	chunk_backp = dsaptr_to_rawptr(list->chunks[list->tail],
+								   shmContext->base);
+
+	*(void **) chunk_backp = context;
+	list->tail++;
+
+	return chunk_backp + sizeof(void *);
+}
+
+
+/*
+ * ShmRetailContextFree
+ *		Frees allocated memory
+ */
+static void
+ShmRetailContextFree(MemoryContext context, void *pointer)
+{
+	ShmRetailContext *shmContext = (ShmRetailContext *) context;
+	chunk_list *list;
+	char *chunk_backp;
+	dsa_pointer dp;
+	int idx;
+
+	/* Rewind to the secret start of the chunk */
+	chunk_backp = (char *) pointer - sizeof(void *);
+
+	dp = rawptr_to_dsaptr(chunk_backp, shmContext->base);
+
+	dsa_free(shm_retail_area, dp);
+
+	/* If global, no need to delete its reference from chunk_list */
+	if (!isLocalShmRetailContext(shmContext))
+		return;
+
+	/* To avoid double free by ShmRetailContextDelete, remove its reference */
+	for (list = shmContext->chunk_list; list != NULL; list = list->next)
+	{
+		for (idx = 0; idx < list->tail; idx++)
+		{
+			if (list->chunks[idx] == dp)
+			{
+				list->chunks[idx] = list->chunks[list->tail - 1];
+				list->tail--;
+				break;
+			}
+		}
+	}
+}
+
+/*
+ * ShmRetailContextRealloc
+ *		When handling repalloc, we simply allocate a new chunk, copy the data
+ *		and discard the old one.
+ */
+static void *
+ShmRetailContextRealloc(MemoryContext context, void *pointer, Size size)
+{
+	elog(ERROR, "ShmRetailContext does not support realloc()");
+	return NULL;				/* keep compiler quiet */
+}
+
+/*
+ * ShmRetailContextGetChunkSpace
+ *		Given a currently-allocated chunk, determine the total space
+ *		it occupies (including all memory-allocation overhead).
+ */
+static Size
+ShmRetailContextGetChunkSpace(MemoryContext context, void *pointer)
+{
+	elog(ERROR, "ShmRetailContext does not support get_chunk_space()");
+	return 0;				/* keep compiler quiet */
+}
+
+/*
+ * ShmRetailContextIsEmpty
+ *		Is an ShmRetailContext empty of any allocated space?
+ */
+static bool
+ShmRetailContextIsEmpty(MemoryContext context)
+{
+	/*
+	 * For now, we say "empty" only if the context is new or just reset. We
+	 * could examine the freelists to determine if all space has been freed,
+	 * but it's not really worth the trouble for present uses of this
+	 * functionality.
+	 */
+	if (context->isReset)
+		return true;
+	return false;
+}
+
+/*
+ * ShmRetailContextStats
+ *		Compute stats about memory consumption of a ShmRetailContext context.
+ *
+ * XXX: can dsa_dump be used?
+ * printfunc: if not NULL, pass a human-readable stats string to this.
+ * passthru: pass this pointer through to printfunc.
+ * totals: if not NULL, add stats about this context into *totals.
+ */
+static void
+ShmRetailContextStats(MemoryContext context,
+		  MemoryStatsPrintFunc printfunc, void *passthru,
+		  MemoryContextCounters *totals)
+{
+	elog(ERROR, "ShmRetailContext does not support stats()");
+}
+
+
+#ifdef MEMORY_CONTEXT_CHECKING
+
+/*
+ * ShmRetailContextCheck
+ *
+ * XXX: for now, do nothing
+ */
+static void
+ShmRetailContextCheck(MemoryContext context)
+{
+
+}
+
+#endif							/* MEMORY_CONTEXT_CHECKING */
diff --git a/src/backend/utils/mmgr/shm_zone.c b/src/backend/utils/mmgr/shm_zone.c
new file mode 100644
index 0000000..dae577a
--- /dev/null
+++ b/src/backend/utils/mmgr/shm_zone.c
@@ -0,0 +1,482 @@
+/*-------------------------------------------------------------------------
+ *
+ * shm_zone.c
+ *	  ShmZoneContext allocator definitions.
+ *
+ * ShmZoneContext is a MemoryContext implementation designed for cases where
+ * you want to allocate and free objects in the shared memory by MemoryContext
+ * API (palloc/pfree). The main use case is global relation cache/plan cache.
+ *
+ * Portions Copyright (c) 2017-2019, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/mmgr/shm_zone.c
+ *
+ */
+
+#include "postgres.h"
+
+#include "lib/ilist.h"
+#include "storage/shmem.h"
+#include "utils/dsa.h"
+#include "utils/memdebug.h"
+#include "utils/memutils.h"
+
+
+#define ShmZone_BLOCKHDRSZ  MAXALIGN(sizeof(ShmZoneBlock))
+#define ShmZone_CHUNKHDRSZ	sizeof(ShmZoneChunk)
+
+typedef struct ShmZoneBlock ShmZoneBlock; /* forward reference */
+typedef struct ShmZoneChunk ShmZoneChunk;
+
+
+/*
+ * ShmZoneContext is a specialized memory context supporting dsa area created
+ * above Postmaster-initialized shared memory.
+ * This context doesn't reuse allocated chunks and blocks, free chunks and
+ * blocks all at once when context is destroyed.
+ */
+typedef struct ShmZoneContext
+{
+	MemoryContextData header;	/* Standard memory-context fields */
+	/* ShmZoneContext parameters */
+	LWLock *child_lock;			/* protect child graph path */
+	void	 *base;				/* raw address of Postmaster-initialized
+								 * shared memory */
+	Size		blockSize; 		/* standard block size */
+	ShmZoneBlock *block; 		/* current (most recently allocated) block */
+	dlist_head blocks; 			/* list of blocks */
+} ShmZoneContext;
+
+
+struct ShmZoneBlock
+{
+	dlist_node	node;			/* doubly-linked list of blocks */
+	Size		blksize;		/* allocated size of this block */
+	char	   *freeptr;		/* start of free space in this block */
+	char	   *endptr;			/* end of space in this block */
+};
+
+
+struct ShmZoneChunk
+{
+	/* size is always the size of the usable space in the chunk */
+	Size		size;
+#define GENERATIONCHUNK_RAWSIZE  (SIZEOF_SIZE_T + SIZEOF_VOID_P)
+	/* ensure proper alignment by adding padding if needed */
+#if (GENERATIONCHUNK_RAWSIZE % MAXIMUM_ALIGNOF) != 0
+	char		padding[MAXIMUM_ALIGNOF - GENERATIONCHUNK_RAWSIZE % MAXIMUM_ALIGNOF];
+#endif
+
+	ShmZoneContext *context; /* owning context, or NULL if freed chunk */
+	/* there must not be any padding to reach a MAXALIGN boundary here! */
+};
+
+
+/*
+ * ShmZoneIsValid
+ *		True iff set is valid allocation set.
+ */
+#define ShmZoneIsValid(context) PointerIsValid(context)
+
+#define ShmZonePointerGetChunk(ptr) \
+	((ShmZoneChunk *)(((char *)(ptr)) - ShmZone_CHUNKHDRSZ))
+#define ShmZoneChunkGetPointer(chk) \
+	((ShmZoneChunk *)(((char *)(chk)) + ShmZone_CHUNKHDRSZ))
+
+
+static inline MemoryContext
+ShmZoneContextCreateInternal(MemoryContext parent, const char *name,
+							 void *base, Size blockSize,
+							 bool startup);
+
+/*
+ * These functions implement the MemoryContext API for ShmZoneContext.
+ */
+static void *ShmZoneContextAlloc(MemoryContext context, Size size);
+static void ShmZoneContextFree(MemoryContext context, void *pointer);
+static void *ShmZoneContextRealloc(MemoryContext context, void *pointer, Size size);
+static void ShmZoneContextReset(MemoryContext context);
+static void ShmZoneContextDelete(MemoryContext context);
+static Size ShmZoneContextGetChunkSpace(MemoryContext context, void *pointer);
+static bool ShmZoneContextIsEmpty(MemoryContext context);
+static void ShmZoneContextStats(MemoryContext context,
+					  MemoryStatsPrintFunc printfunc, void *passthru,
+					  MemoryContextCounters *totals);
+#ifdef MEMORY_CONTEXT_CHECKING
+static void ShmZoneContextCheck(MemoryContext context);
+#endif
+
+/*
+ * This is the virtual function table for ShmZoneContext contexts.
+ */
+static const MemoryContextMethods ShmZoneContextMethods = {
+	ShmZoneContextAlloc,
+	ShmZoneContextFree,
+	ShmZoneContextRealloc,
+	ShmZoneContextReset,
+	ShmZoneContextDelete,
+	ShmZoneContextGetChunkSpace,
+	ShmZoneContextIsEmpty,
+	ShmZoneContextStats
+#ifdef MEMORY_CONTEXT_CHECKING
+	,ShmZoneContextCheck
+#endif
+};
+
+static dsa_area *shm_zone_area = NULL;
+
+/* Get DSA area of global meta cache */
+void
+ShmZoneContextSetDSAarea(dsa_area *area)
+{
+	if (!shm_zone_area)
+		shm_zone_area = area;
+}
+
+
+/*
+ * ShmZoneContextCreateOrigin
+ *		Create a new permanent ShmZoneContext context.
+ *
+ */
+MemoryContext
+ShmZoneContextCreateOrigin(MemoryContext parent,
+						   const char *name,
+						   void *base)
+{
+	/* XXX: use more appropriate size */
+	return ShmZoneContextCreateInternal(parent, name, base,
+										ShmZone_DEFAULT_BLOCK_SIZE,
+										true);
+}
+
+MemoryContext
+ShmZoneContextCreateClone(MemoryContext parent,
+						   const char *name,
+						   MemoryContext origin)
+{
+	ShmZoneContext *shmContext = (ShmZoneContext *) origin;
+
+	return ShmZoneContextCreateInternal(parent, name,
+										shmContext->base,
+										ShmZone_DEFAULT_BLOCK_SIZE,
+										false);
+}
+
+/*
+ * ShmZoneContextCreateInternal
+ *		Work-horse for ShmZoneContextCreateOrigin/ShmZoneContextCreateClone
+ *
+ * parent: parent context, or NULL if top-level context
+ * name: name of context (must be statically allocated)
+ * area: dsa_area created in place of Postmaster-initialized shared memory
+ * base: address of Postmaster-initialized shared memory
+ * isLocal: context is local?
+ *
+ */
+static inline MemoryContext
+ShmZoneContextCreateInternal(MemoryContext parent, const char *name,
+							 void *base, Size blockSize,
+							 bool startup)
+{
+	ShmZoneContext *shmContext;
+	LWLock *child_lock;
+	LWLock *context_lock;
+
+	/*
+	 * If it's Postgres start up time, allocate context object itself
+	 * by ShmemInitstruct. Otherwise, use  ShmZoneContextAlloc via palloc.
+	 */
+	if (startup)
+	{
+		bool found;
+
+		shmContext = (ShmZoneContext *)
+			ShmemInitStruct(name, ShmZoneContextSize(), &found);
+
+		/*
+		 * Register MemoryContextMethods with methods table. This should be
+		 * called by each process in Windows or EXEC-BACKEND case.
+		 */
+		RegisterMemoryContextMethods(MEMORY_CONTEXT_SHARED_ZONE,
+									 &ShmZoneContextMethods);
+
+		if (found)
+			return &shmContext->header;
+		else
+		{
+			child_lock = (LWLock *)
+				MAXALIGN(((char *)shmContext) + sizeof(ShmZoneContext));
+
+			LWLockInitialize(child_lock, LWTRANCHE_SHARED_MCXT);
+			shmContext->child_lock = child_lock;
+		}
+	}
+	else
+	{
+		/* Caller should switch to T_ShmZoneContext context */
+		Assert(nodeTag(CurrentMemoryContext) == T_ShmZoneContext);
+		AssertArg(parent);
+
+		/* Use parent context's child_lock */
+		shmContext = palloc0(sizeof(ShmZoneContext));
+		shmContext->child_lock = ShmContextGetLock(parent);
+	}
+
+	shmContext->base = base;
+	shmContext->blockSize = blockSize;
+	shmContext->block = NULL;
+	dlist_init(&shmContext->blocks);
+
+	/* Get parent context's child path lock */
+	if (parent != NULL)
+	{
+		context_lock = ShmContextGetLock(parent);
+		LWLockAcquire(context_lock, LW_EXCLUSIVE);
+	}
+
+	MemoryContextCreate(&shmContext->header,
+						T_ShmZoneContext,
+						&ShmZoneContextMethods,
+						parent,
+						name);
+
+	if (parent != NULL)
+		LWLockRelease(context_lock);
+
+	return &shmContext->header;
+}
+
+/*
+ * ShmZoneContextSize
+ *		Size of ShmZoneContext
+ * If you use ShmZoneContextCreateGlobal, this fucntion should be called
+ * to reserve the size of ShmZoneContext at postgres start-up time
+ */
+Size
+ShmZoneContextSize(void)
+{
+	return sizeof(ShmZoneContext) + MAXIMUM_ALIGNOF + sizeof(LWLock);
+}
+
+
+
+/*
+ * ShmZoneContextReset
+ *		Free all the memory registered in chunk_list and list
+ *
+ * The chunks registered in chunk_list are dsa_freed.
+ * This does not affect permanent context.
+ *
+ */
+static void
+ShmZoneContextReset(MemoryContext context)
+{
+	ShmZoneContext *shmContext = (ShmZoneContext *) context;
+	dsa_pointer dsa_blockptr;
+	dlist_mutable_iter miter;
+
+	AssertArg(ShmZoneIsValid(shmContext));
+
+	dlist_foreach_modify(miter, &shmContext->blocks)
+	{
+		ShmZoneBlock *block = dlist_container(ShmZoneBlock, node, miter.cur);
+		dlist_delete(miter.cur);
+
+		context->mem_allocated -= block->blksize;
+
+		dsa_blockptr = rawptr_to_dsaptr(block, shmContext->base);
+
+		dsa_free(shm_zone_area, dsa_blockptr);
+	}
+
+	shmContext->block = NULL;
+
+	Assert(dlist_is_empty(&shmContext->blocks));
+}
+
+/*
+ * ShmZoneContextDelete
+ *		Free all the memory registered in chunk_list and context.
+ *		See ShmZoneContextReset.
+ */
+static void
+ShmZoneContextDelete(MemoryContext context)
+{
+	/* Reset to release all the chunk_lists */
+	ShmZoneContextReset(context);
+	/* And free the context header */
+	pfree(context);
+}
+
+/*
+ * ShmZoneContextAlloc
+ *		Returns native pointer to allocated memory
+ * This function assumes that the parent of this context is located in
+ * local memory. Since only the process which created the context can
+ * allocate memory, acquiring a child_lock is not necessary here. After this
+ * context is re-parent to shared context, no allocation is allowed.
+ */
+static void *
+ShmZoneContextAlloc(MemoryContext context, Size size)
+{
+	ShmZoneContext *shmContext = (ShmZoneContext *) context;
+	ShmZoneBlock *block;
+	ShmZoneChunk *chunk;
+	Size		chunk_size = MAXALIGN(size);
+
+	if (chunk_size > shmContext->blockSize / 8)
+	{
+		dsa_pointer dsa_blockptr;
+		Size blksize = chunk_size + ShmZone_BLOCKHDRSZ + ShmZone_CHUNKHDRSZ;
+
+		dsa_blockptr = dsa_allocate(shm_zone_area,
+									sizeof(void *) + size);
+
+		if (DsaPointerIsValid(dsa_blockptr))
+			return NULL;
+
+		context->mem_allocated += blksize;
+
+		block = (ShmZoneBlock *) dsaptr_to_rawptr(dsa_blockptr, shmContext->base);
+
+		block->blksize = blksize;
+
+		block->freeptr = block->endptr = ((char *) block) + blksize;
+
+		chunk = (ShmZoneChunk *) (((char *) block) + ShmZone_BLOCKHDRSZ);
+		chunk->context = shmContext;
+		chunk->size = chunk_size;
+
+		/* add the block to the list of allocated blocks */
+		dlist_push_head(&shmContext->blocks, &block->node);
+
+		return ShmZoneChunkGetPointer(chunk);
+	}
+
+	block = shmContext->block;
+
+	if ((block == NULL) ||
+		(block->endptr - block->freeptr) < ShmZone_CHUNKHDRSZ + chunk_size)
+	{
+		dsa_pointer dsa_blockptr;
+		Size blksize = shmContext->blockSize;
+
+		dsa_blockptr = dsa_allocate(shm_zone_area,
+									sizeof(void *) + size);
+
+		if (DsaPointerIsValid(dsa_blockptr))
+			return NULL;
+
+		context->mem_allocated += blksize;
+
+		block = (ShmZoneBlock *) dsaptr_to_rawptr(dsa_blockptr, shmContext->base);
+
+		block->blksize = blksize;
+
+		block->freeptr = ((char *) block) + ShmZone_BLOCKHDRSZ;
+		block->endptr = ((char *) block) + blksize;
+
+		/* add it to the doubly-linked list of blocks */
+		dlist_push_head(&shmContext->blocks, &block->node);
+
+		/* and also use it as the current allocation block */
+		shmContext->block = block;
+
+	}
+
+	Assert(block != NULL);
+	Assert(block->endptr - block->freeptr >= ShmZone_CHUNKHDRSZ + chunk_size);
+
+	chunk = (ShmZoneChunk *)block->freeptr;
+
+	block->freeptr += (ShmZone_CHUNKHDRSZ + chunk_size);
+
+	chunk->size = chunk_size;
+	chunk->context = shmContext;
+
+	return ShmZoneChunkGetPointer(chunk);
+}
+
+
+/*
+ * ShmZoneContextFree
+ *		Do nothing. Free chunks and blocks all at once when context
+ *		is destroyed.
+ */
+static void
+ShmZoneContextFree(MemoryContext context, void *pointer)
+{
+	exit(0);
+}
+
+/*
+ * ShmZoneContextRealloc
+ *
+ *	realloc() is not supported
+ */
+static void *
+ShmZoneContextRealloc(MemoryContext context, void *pointer, Size size)
+{
+	elog(ERROR, "ShmZoneContext does not support realloc()");
+	return NULL;				/* keep compiler quiet */
+}
+
+/*
+ * ShmZoneContextGetChunkSpace
+ *		Given a currently-allocated chunk, determine the total space
+ *		it occupies (including all memory-allocation overhead).
+ */
+static Size
+ShmZoneContextGetChunkSpace(MemoryContext context, void *pointer)
+{
+	ShmZoneChunk *chunk = ShmZonePointerGetChunk(pointer);
+
+	return chunk->size + ShmZone_CHUNKHDRSZ;
+}
+
+/*
+ * ShmZoneContextIsEmpty
+ *		Is an ShmZoneContext empty of any allocated space?
+ */
+static bool
+ShmZoneContextIsEmpty(MemoryContext context)
+{
+	ShmZoneContext *shmContext = (ShmZoneContext *) context;
+
+	return dlist_is_empty(&shmContext->blocks);
+}
+
+/*
+ * ShmZoneContextStats
+ *		Compute stats about memory consumption of a ShmZoneContext context.
+ *
+ * XXX: can dsa_dump be used?
+ * printfunc: if not NULL, pass a human-readable stats string to this.
+ * passthru: pass this pointer through to printfunc.
+ * totals: if not NULL, add stats about this context into *totals.
+ */
+static void
+ShmZoneContextStats(MemoryContext context,
+		  MemoryStatsPrintFunc printfunc, void *passthru,
+		  MemoryContextCounters *totals)
+{
+	elog(ERROR, "ShmZoneContext does not support stats()");
+}
+
+
+#ifdef MEMORY_CONTEXT_CHECKING
+
+/*
+ * ShmZoneContextCheck
+ *
+ * XXX: for now, do nothing
+ */
+static void
+ShmZoneContextCheck(MemoryContext context)
+{
+
+}
+
+#endif							/* MEMORY_CONTEXT_CHECKING */
diff --git a/src/include/nodes/memnodes.h b/src/include/nodes/memnodes.h
index c9f2bbc..23cefd6 100644
--- a/src/include/nodes/memnodes.h
+++ b/src/include/nodes/memnodes.h
@@ -72,6 +72,16 @@ typedef struct MemoryContextMethods
 #endif
 } MemoryContextMethods;
 
+/* General utilities for ShmRetailContext and ShmZoneContext */
+//#define InvalidShmContextMethodsID ((ShmContextMethodsID) -1)
+
+typedef enum ShmContextMethodsID
+{
+	InvalidShmContextMethodsID = -1,
+	MEMORY_CONTEXT_SHARED_RETAIL,
+	MEMORY_CONTEXT_SHARED_ZONE,
+	MEMORY_CONTEXT_METHODS_ID_MAX
+} ShmContextMethodsID;
 
 typedef struct MemoryContextData
 {
@@ -88,6 +98,12 @@ typedef struct MemoryContextData
 	const char *name;			/* context name (just for debugging) */
 	const char *ident;			/* context ID if any (just for debugging) */
 	MemoryContextCallback *reset_cbs;	/* list of reset/delete callbacks */
+	/*
+	 * InvalidId and use methods if context is not located in shared memory
+	 * otherwise, ID for method table is set and used since function pointers
+	 * cannot be shared.
+	 */
+	ShmContextMethodsID methodsID;
 } MemoryContextData;
 
 /* utils/palloc.h contains typedef struct MemoryContextData *MemoryContext */
@@ -103,6 +119,20 @@ typedef struct MemoryContextData
 	((context) != NULL && \
 	 (IsA((context), AllocSetContext) || \
 	  IsA((context), SlabContext) || \
-	  IsA((context), GenerationContext)))
+	  IsA((context), GenerationContext) || \
+	  IsA((context), ShmRetailContext) || \
+	  IsA((context), ShmZoneContext)))
+
+/*
+ * MemoryContextIsShared
+ *		True iff memory context is shared.
+ *
+ * Add new context types to the set accepted by this macro.
+ */
+#define MemoryContextIsShared(context) \
+	((context) != NULL && \
+	 (IsA((context), ShmRetailContext) || \
+	  IsA((context), ShmZoneContext)))
+
 
 #endif							/* MEMNODES_H */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index baced7e..a104575 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -280,6 +280,8 @@ typedef enum NodeTag
 	T_AllocSetContext,
 	T_SlabContext,
 	T_GenerationContext,
+	T_ShmRetailContext,
+	T_ShmZoneContext,
 
 	/*
 	 * TAGS FOR VALUE NODES (value.h)
diff --git a/src/include/utils/memutils.h b/src/include/utils/memutils.h
index 909bc2e..521e085 100644
--- a/src/include/utils/memutils.h
+++ b/src/include/utils/memutils.h
@@ -18,7 +18,8 @@
 #define MEMUTILS_H
 
 #include "nodes/memnodes.h"
-
+#include "utils/dsa.h"
+#include "storage/lwlock.h"
 
 /*
  * MaxAllocSize, MaxAllocHugeSize
@@ -59,6 +60,9 @@ extern PGDLLIMPORT MemoryContext CacheMemoryContext;
 extern PGDLLIMPORT MemoryContext MessageContext;
 extern PGDLLIMPORT MemoryContext TopTransactionContext;
 extern PGDLLIMPORT MemoryContext CurTransactionContext;
+extern PGDLLIMPORT MemoryContext GlobalCacheContext;
+extern PGDLLIMPORT MemoryContext GlobalCatCacheContext;
+extern PGDLLIMPORT MemoryContext GlobalRelCacheContext;
 
 /* This is a transient link to the active portal's memory context: */
 extern PGDLLIMPORT MemoryContext PortalContext;
@@ -79,6 +83,8 @@ extern void MemoryContextDeleteChildren(MemoryContext context);
 extern void MemoryContextSetIdentifier(MemoryContext context, const char *id);
 extern void MemoryContextSetParent(MemoryContext context,
 								   MemoryContext new_parent);
+extern MemoryContext MemoryContextClone(MemoryContext template,
+										MemoryContext parent);
 extern Size GetMemoryChunkSpace(void *pointer);
 extern MemoryContext MemoryContextGetParent(MemoryContext context);
 extern bool MemoryContextIsEmpty(MemoryContext context);
@@ -183,6 +189,70 @@ extern MemoryContext GenerationContextCreate(MemoryContext parent,
 											 Size blockSize);
 
 /*
+ * Shared memory cannot use MemoryContextMethods pointers as general memory
+ * context because function pointers can not be generally shared among process.
+ * So each process needs to register function pointers from this table and
+ * use this method instead.
+ */
+MemoryContextMethods
+shmcxt_methods[MEMORY_CONTEXT_METHODS_ID_MAX];
+
+static inline void
+RegisterMemoryContextMethods(ShmContextMethodsID id,
+							 const MemoryContextMethods *methods)
+{
+	shmcxt_methods[id] = *methods;
+}
+
+/* utility for both shm_retail and shm_zone */
+extern void MemoryContextSetShmContextMethodsID(MemoryContext node,
+												ShmContextMethodsID id);
+extern LWLock* ShmContextGetLock(MemoryContext context);
+
+extern void GlobalCacheContextShmemInit(Size size);
+extern void GlobalCatCacheContextShmemInit(void);
+extern void GlobalRelCacheContextShmemInit(void);
+
+extern Size GlobalCacheContextSize(void);
+extern Size GlobalCatCacheContextSize(void);
+extern Size GlobalRelCacheContextSize(void);
+
+#define dsaptr_to_rawptr(dsa_p, base_p)	\
+	((char *)(base_p) + (dsa_p))
+#define rawptr_to_dsaptr(raw_p, base_p)	\
+	((dsa_pointer) ((char *)(raw_p) - (char *)(base_p)))
+
+
+/* shm_retail.c */
+extern MemoryContext ShmRetailContextCreateGlobal(MemoryContext parent,
+												  const char *name,
+												  void *base);
+extern MemoryContext ShmRetailContextCreateLocal(MemoryContext parent,
+												 const char *name,
+												 MemoryContext global_context);
+
+extern void ShmRetailContextMoveChunk(MemoryContext local_context,
+									  MemoryContext global_context);
+
+extern Size ShmRetailContextSize(void);
+extern void ShmRetailContextSetDSAarea(dsa_area *area);
+extern dsa_area *ShmRetailContextGetDSAarea(void);
+
+/* shm_zone.c */
+extern MemoryContext ShmZoneContextCreateOrigin(MemoryContext parent,
+												const char *name,
+												void *base);
+
+extern MemoryContext ShmZoneContextCreateClone(MemoryContext parent,
+											   const char *name,
+											   MemoryContext origin);
+
+extern Size ShmZoneContextSize(void);
+extern void ShmZoneContextSetDSAarea(dsa_area *area);
+
+
+
+/*
  * Recommended default alloc parameters, suitable for "ordinary" contexts
  * that might hold quite a lot of data.
  */
@@ -221,4 +291,6 @@ extern MemoryContext GenerationContextCreate(MemoryContext parent,
 #define SLAB_DEFAULT_BLOCK_SIZE		(8 * 1024)
 #define SLAB_LARGE_BLOCK_SIZE		(8 * 1024 * 1024)
 
+#define ShmZone_DEFAULT_BLOCK_SIZE (1 * 1024)
+
 #endif							/* MEMUTILS_H */
