diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index bdc34cf94e..f818aba974 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -508,6 +508,19 @@
       </para></entry>
      </row>
 
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>path</structfield> <type>int4[]</type>
+      </para>
+      <para>
+       Path that includes all contexts from TopMemoryContext to the current
+       context. Context IDs in this list represents all parents of a context,
+       and last element in the list refers to the context itself. This can be
+       used to build the parent and child relation. Note that IDs used in path
+       are transient and may change in each execution
+      </para></entry>
+     </row>
+
      <row>
       <entry role="catalog_table_entry"><para role="column_definition">
        <structfield>total_bytes</structfield> <type>int8</type>
@@ -561,6 +574,25 @@
    read only by superusers or roles with the privileges of the
    <literal>pg_read_all_stats</literal> role.
   </para>
+
+  <para>
+   The <structfield>path</structfield> column can be useful to build
+   parent/child relation between memory contexts. For example, the following
+   query calculates the total number of bytes used by a memory context and its
+   child contexts:
+<programlisting>
+WITH memory_contexts AS (
+    SELECT * 
+    FROM pg_backend_memory_contexts
+)
+SELECT SUM(total_bytes)
+FROM memory_contexts
+WHERE ARRAY[(SELECT path[array_length(path, 1)] FROM memory_contexts WHERE name = 'CacheMemoryContext')] &lt;@ path;
+</programlisting>
+    Also, <link linkend="queries-with">Common Table Expressions</link> can be
+    useful while working with context IDs as these IDs are temporary and may
+    change in each invocation.
+  </para>
  </sect1>
 
  <sect1 id="view-pg-config">
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index 1085941484..b0b4b1aebf 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -17,9 +17,12 @@
 
 #include "funcapi.h"
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "utils/array.h"
 #include "utils/builtins.h"
+#include "utils/hsearch.h"
 
 /* ----------
  * The max bytes for showing identifiers of MemoryContext.
@@ -27,47 +30,101 @@
  */
 #define MEMORY_CONTEXT_IDENT_DISPLAY_SIZE	1024
 
+typedef struct MemoryContextId
+{
+	MemoryContext context;
+	int context_id;
+} MemoryContextId;
+
+/*
+ * get_memory_context_name_and_indent
+ *		Populate *name and *ident from the name and ident from 'context'.
+ */
+static void
+get_memory_context_name_and_indent(MemoryContext context, const char **name,
+								   const char **ident)
+{
+	*name = context->name;
+	*ident = context->ident;
+
+	/*
+	* To be consistent with logging output, we label dynahash contexts with
+	* just the hash table name as with MemoryContextStatsPrint().
+	*/
+	if (ident && strcmp(*name, "dynahash") == 0)
+	{
+		*name = *ident;
+		*ident = NULL;
+	}
+}
+
+/*
+ * int_list_to_array
+ *		Convert a IntList to an int[] array.
+ */
+static Datum
+int_list_to_array(List *list)
+{
+	Datum *datum_array;
+	int length;
+	ArrayType *result_array;
+
+	length = list_length(list);
+	datum_array = (Datum *) palloc(length * sizeof(Datum));
+	length = 0;
+	foreach_int(id, list)
+		datum_array[length++] = Int32GetDatum(id);
+
+	result_array = construct_array_builtin(datum_array, length, INT4OID);
+
+	return PointerGetDatum(result_array);
+}
+
 /*
  * PutMemoryContextsStatsTupleStore
- *		One recursion level for pg_get_backend_memory_contexts.
+ *		Add details for the given MemoryContext to 'tupstore'.
  */
 static void
 PutMemoryContextsStatsTupleStore(Tuplestorestate *tupstore,
-								 TupleDesc tupdesc, MemoryContext context,
-								 const char *parent, int level)
+								 TupleDesc tupdesc, MemoryContext context, HTAB *context_id_lookup)
 {
-#define PG_GET_BACKEND_MEMORY_CONTEXTS_COLS	10
+#define PG_GET_BACKEND_MEMORY_CONTEXTS_COLS	11
 
 	Datum		values[PG_GET_BACKEND_MEMORY_CONTEXTS_COLS];
 	bool		nulls[PG_GET_BACKEND_MEMORY_CONTEXTS_COLS];
 	MemoryContextCounters stat;
-	MemoryContext child;
+	List *path = NIL;
 	const char *name;
 	const char *ident;
 	const char *type;
 
 	Assert(MemoryContextIsValid(context));
 
-	name = context->name;
-	ident = context->ident;
-
 	/*
-	 * To be consistent with logging output, we label dynahash contexts with
-	 * just the hash table name as with MemoryContextStatsPrint().
+	 * Figure out the transient context_id of this context and each of its
+	 * ancestors.
 	 */
-	if (ident && strcmp(name, "dynahash") == 0)
+	for (MemoryContext cur = context; cur != NULL; cur = cur->parent)
 	{
-		name = ident;
-		ident = NULL;
+		MemoryContextId *entry;
+		bool	found;
+
+		entry = hash_search(context_id_lookup, &cur, HASH_FIND, &found);
+
+		if (!found)
+			elog(ERROR, "hash table corrupted");
+		path = lcons_int(entry->context_id, path);
 	}
 
 	/* Examine the context itself */
 	memset(&stat, 0, sizeof(stat));
-	(*context->methods->stats) (context, NULL, (void *) &level, &stat, true);
+	(*context->methods->stats)(context, NULL, NULL, &stat, true);
 
 	memset(values, 0, sizeof(values));
 	memset(nulls, 0, sizeof(nulls));
 
+	get_memory_context_name_and_indent(context, &name, &ident);
+
 	if (name)
 		values[0] = CStringGetTextDatum(name);
 	else
@@ -75,15 +132,17 @@ PutMemoryContextsStatsTupleStore(Tuplestorestate *tupstore,
 
 	if (ident)
 	{
-		int			idlen = strlen(ident);
-		char		clipped_ident[MEMORY_CONTEXT_IDENT_DISPLAY_SIZE];
+		int idlen = strlen(ident);
+		char clipped_ident[MEMORY_CONTEXT_IDENT_DISPLAY_SIZE];
 
 		/*
-		 * Some identifiers such as SQL query string can be very long,
-		 * truncate oversize identifiers.
-		 */
+		* Some identifiers such as SQL query string can be very long,
+		* truncate oversize identifiers.
+		*/
 		if (idlen >= MEMORY_CONTEXT_IDENT_DISPLAY_SIZE)
-			idlen = pg_mbcliplen(ident, idlen, MEMORY_CONTEXT_IDENT_DISPLAY_SIZE - 1);
+			idlen = pg_mbcliplen(ident,
+									idlen,
+									MEMORY_CONTEXT_IDENT_DISPLAY_SIZE - 1);
 
 		memcpy(clipped_ident, ident, idlen);
 		clipped_ident[idlen] = '\0';
@@ -92,8 +151,15 @@ PutMemoryContextsStatsTupleStore(Tuplestorestate *tupstore,
 	else
 		nulls[1] = true;
 
-	if (parent)
-		values[2] = CStringGetTextDatum(parent);
+	if (context->parent)
+	{
+		char *parent_name, *parent_ident;
+
+		get_memory_context_name_and_indent(context->parent,
+										   &parent_name,
+										   &parent_ident);
+		values[2] = CStringGetTextDatum(parent_name);
+	}
 	else
 		nulls[2] = true;
 
@@ -117,19 +183,16 @@ PutMemoryContextsStatsTupleStore(Tuplestorestate *tupstore,
 	}
 
 	values[3] = CStringGetTextDatum(type);
-	values[4] = Int32GetDatum(level);
-	values[5] = Int64GetDatum(stat.totalspace);
-	values[6] = Int64GetDatum(stat.nblocks);
-	values[7] = Int64GetDatum(stat.freespace);
-	values[8] = Int64GetDatum(stat.freechunks);
-	values[9] = Int64GetDatum(stat.totalspace - stat.freespace);
-	tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+	values[4] = Int32GetDatum(list_length(path)); /* level */
+	values[5] = int_list_to_array(path);
+	values[6] = Int64GetDatum(stat.totalspace);
+	values[7] = Int64GetDatum(stat.nblocks);
+	values[8] = Int64GetDatum(stat.freespace);
+	values[9] = Int64GetDatum(stat.freechunks);
+	values[10] = Int64GetDatum(stat.totalspace - stat.freespace);
 
-	for (child = context->firstchild; child != NULL; child = child->nextchild)
-	{
-		PutMemoryContextsStatsTupleStore(tupstore, tupdesc,
-										 child, name, level + 1);
-	}
+	tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+	list_free(path);
 }
 
 /*
@@ -140,10 +203,76 @@ Datum
 pg_get_backend_memory_contexts(PG_FUNCTION_ARGS)
 {
 	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	int context_id;
+	List *queue;
+	HASHCTL ctl;
+	HTAB *context_id_lookup;
+
+	ctl.keysize = sizeof(MemoryContext);
+	ctl.entrysize = sizeof(MemoryContextId);
+
+	context_id_lookup = hash_create("pg_get_backend_memory_contexts lookup",
+									256,
+									&ctl,
+									HASH_ELEM | HASH_BLOBS);
 
 	InitMaterializedSRF(fcinfo, 0);
-	PutMemoryContextsStatsTupleStore(rsinfo->setResult, rsinfo->setDesc,
-									 TopMemoryContext, NULL, 0);
+
+	/*
+	 * Here we use a non-recursive algorithm to visit all MemoryContexts
+	 * starting with TopMemoryContext.  The reason we avoid using a recursive
+	 * algorithm is because we want to assign the context_id breadth first.
+	 * I.e. all context at level 1 are assigned ids before contexts at level 2.
+	 * Because lower-leveled contexts are less likely to change, this makes the
+	 * assigned context_id more stable.  Otherwise, if the first child of
+	 * TopMemoryContext obtained an additional grand child, the context_id for
+	 * the second child of TopMemoryContext would change.
+	 */
+	queue = list_make1(TopMemoryContext);
+
+	/* TopMemoryContext will always have a context_id of 1 */
+	context_id = 1;
+
+	while (queue != NIL)
+	{
+		List *nextQueue = NIL;
+		ListCell *lc;
+
+		foreach(lc, queue)
+		{
+			MemoryContext cur = lfirst(lc);
+			MemoryContextId *entry;
+			bool found;
+
+			/*
+			 * Record the context_id that we've assigned to each MemoryContext.
+			 * PutMemoryContextsStatsTupleStore needs this to populate the "path"
+			 * column with the parent context_ids.
+			 */
+			entry = (MemoryContextId *) hash_search(context_id_lookup, &cur,
+													HASH_ENTER, &found);
+			entry->context_id = context_id++;
+			Assert(!found);
+
+			PutMemoryContextsStatsTupleStore(rsinfo->setResult,
+											 rsinfo->setDesc,
+											 cur,
+											 context_id_lookup);
+
+			/*
+			 * Queue up all the child contexts of this level for the next
+			 * iteration of the outer loop.
+			 */
+			for (MemoryContext c = cur->firstchild; c != NULL; c = c->nextchild)
+				nextQueue = lappend(nextQueue, c);
+		}
+
+		/* process the next level down */
+		list_free(queue);
+		queue = nextQueue;
+	}
+
+	hash_destroy(context_id_lookup);
 
 	return (Datum) 0;
 }
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index e899ed5e77..4a48f19da7 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8284,9 +8284,9 @@
   proname => 'pg_get_backend_memory_contexts', prorows => '100',
   proretset => 't', provolatile => 'v', proparallel => 'r',
   prorettype => 'record', proargtypes => '',
-  proallargtypes => '{text,text,text,text,int4,int8,int8,int8,int8,int8}',
-  proargmodes => '{o,o,o,o,o,o,o,o,o,o}',
-  proargnames => '{name, ident, parent, type, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes}',
+  proallargtypes => '{text,text,text,text,int4,_int4,int8,int8,int8,int8,int8}',
+  proargmodes => '{o,o,o,o,o,o,o,o,o,o,o}',
+  proargnames => '{name, ident, parent, type, level, path, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes}',
   prosrc => 'pg_get_backend_memory_contexts' },
 
 # logging memory contexts of the specified backend
diff --git a/src/include/nodes/memnodes.h b/src/include/nodes/memnodes.h
index c4c9fd3e3e..addfbb1ccd 100644
--- a/src/include/nodes/memnodes.h
+++ b/src/include/nodes/memnodes.h
@@ -128,8 +128,8 @@ typedef struct MemoryContextData
 	MemoryContext firstchild;	/* head of linked list of children */
 	MemoryContext prevchild;	/* previous child of same parent */
 	MemoryContext nextchild;	/* next child of same parent */
-	const char *name;			/* context name (just for debugging) */
-	const char *ident;			/* context ID if any (just for debugging) */
+	const char *name;			/* context name */
+	const char *ident;			/* context ID if any */
 	MemoryContextCallback *reset_cbs;	/* list of reset/delete callbacks */
 } MemoryContextData;
 
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 4c789279e5..5201280669 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1308,12 +1308,13 @@ pg_backend_memory_contexts| SELECT name,
     parent,
     type,
     level,
+    path,
     total_bytes,
     total_nblocks,
     free_bytes,
     free_chunks,
     used_bytes
-   FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, parent, type, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes);
+   FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, parent, type, level, path, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes);
 pg_config| SELECT name,
     setting
    FROM pg_config() pg_config(name, setting);
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index 729620de13..b27b7dca4b 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -51,6 +51,19 @@ from pg_backend_memory_contexts where name = 'Caller tuples';
 (1 row)
 
 rollback;
+-- Test whether there are contexts with CacheMemoryContext in their path.
+-- There should be multiple children of CacheMemoryContext.
+with contexts as (
+  select * from pg_backend_memory_contexts
+)
+select count(*) > 0
+from contexts
+where array[(select path[level+1] from contexts where name = 'CacheMemoryContext')] <@ path;
+ ?column? 
+----------
+ t
+(1 row)
+
 -- At introduction, pg_config had 23 entries; it may grow
 select count(*) > 20 as ok from pg_config;
  ok 
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index 7edac2fde1..0953d3e2c7 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -32,6 +32,15 @@ select type, name, parent, total_bytes > 0, total_nblocks, free_bytes > 0, free_
 from pg_backend_memory_contexts where name = 'Caller tuples';
 rollback;
 
+-- Test whether there are contexts with CacheMemoryContext in their path.
+-- There should be multiple children of CacheMemoryContext.
+with contexts as (
+  select * from pg_backend_memory_contexts
+)
+select count(*) > 0
+from contexts
+where array[(select path[level+1] from contexts where name = 'CacheMemoryContext')] <@ path;
+
 -- At introduction, pg_config had 23 entries; it may grow
 select count(*) > 20 as ok from pg_config;
 
