From e448d8b3a941ecab539bdc57151be9f3968ca3de Mon Sep 17 00:00:00 2001
From: Melih Mutlu <m.melihmutlu@gmail.com>
Date: Mon, 1 Jul 2024 16:06:03 +0300
Subject: [PATCH v7 2/2] Add total_bytes_including_children column

Add a new column total_bytes_including_children into
pg_backend_memory_contexts.
---
 doc/src/sgml/system-views.sgml         |  9 +++++++++
 src/backend/utils/adt/mcxtfuncs.c      | 25 ++++++++++++++++---------
 src/include/catalog/pg_proc.dat        |  6 +++---
 src/test/regress/expected/rules.out    |  3 ++-
 src/test/regress/expected/sysviews.out | 14 ++++++++++++++
 src/test/regress/sql/sysviews.sql      | 10 ++++++++++
 6 files changed, 54 insertions(+), 13 deletions(-)

diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index 501244709d..0d6ad8e55a 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -531,6 +531,15 @@
       </para></entry>
      </row>
 
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>total_bytes_including_children</structfield> <type>int8</type>
+      </para>
+      <para>
+       Total bytes allocated for this memory context including its children
+      </para></entry>
+     </row>
+
      <row>
       <entry role="catalog_table_entry"><para role="column_definition">
        <structfield>total_nblocks</structfield> <type>int8</type>
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index 98f03b3279..db947cdc3e 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -38,9 +38,10 @@ static void
 PutMemoryContextsStatsTupleStore(Tuplestorestate *tupstore,
 								 TupleDesc tupdesc, MemoryContext context,
 								 const char *parent, int level, int *context_id,
-								 List *context_ids)
+								 List *context_ids,
+								 Size *total_bytes_inc_children)
 {
-#define PG_GET_BACKEND_MEMORY_CONTEXTS_COLS	11
+#define PG_GET_BACKEND_MEMORY_CONTEXTS_COLS	12
 
 	Datum		values[PG_GET_BACKEND_MEMORY_CONTEXTS_COLS];
 	bool		nulls[PG_GET_BACKEND_MEMORY_CONTEXTS_COLS];
@@ -50,6 +51,7 @@ PutMemoryContextsStatsTupleStore(Tuplestorestate *tupstore,
 	const char *ident;
 	const char *type;
 	int  current_context_id = (*context_id)++;
+	Size total_bytes_children = 0;
 
 	Assert(MemoryContextIsValid(context));
 
@@ -127,21 +129,25 @@ PutMemoryContextsStatsTupleStore(Tuplestorestate *tupstore,
 	context_ids = lappend_int(context_ids, current_context_id);
 	values[5] = convert_ids_to_datum(context_ids);
 
+	total_bytes_children += stat.totalspace;
 	for (child = context->firstchild; child != NULL; child = child->nextchild)
 	{
 		PutMemoryContextsStatsTupleStore(tupstore, tupdesc, child, name,
-										 level+1, context_id, context_ids);
+										 level+1, context_id, context_ids,
+										 &total_bytes_children);
 	}
 	context_ids = list_delete_last(context_ids);
 
 	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);
-
+	values[7] = Int64GetDatum(total_bytes_children);
+	values[8] = Int64GetDatum(stat.nblocks);
+	values[9] = Int64GetDatum(stat.freespace);
+	values[10] = Int64GetDatum(stat.freechunks);
+	values[11] = Int64GetDatum(stat.totalspace - stat.freespace);
 
 	tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+
+	*total_bytes_inc_children += total_bytes_children;
 }
 
 /*
@@ -154,11 +160,12 @@ pg_get_backend_memory_contexts(PG_FUNCTION_ARGS)
 	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
 	int context_id = 0;
 	List *context_ids = NIL;
+	Size total_bytes_inc_children = 0;
 
 	InitMaterializedSRF(fcinfo, 0);
 	PutMemoryContextsStatsTupleStore(rsinfo->setResult, rsinfo->setDesc,
 									 TopMemoryContext, NULL, 0, &context_id,
-									 context_ids);
+									 context_ids, &total_bytes_inc_children);
 
 	return (Datum) 0;
 }
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 18d0187a80..8bf8253ab7 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,_int4,int8,int8,int8,int8,int8}',
-  proargmodes => '{o,o,o,o,o,o,o,o,o,o,o}',
-  proargnames => '{name, ident, parent, type, level, context_ids, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes}',
+  proallargtypes => '{text,text,text,text,int4,_int4,int8,int8,int8,int8,int8,int8}',
+  proargmodes => '{o,o,o,o,o,o,o,o,o,o,o,o}',
+  proargnames => '{name, ident, parent, type, level, context_ids, total_bytes, total_bytes_including_children, 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/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 7593e94939..35ffd387c7 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1310,11 +1310,12 @@ pg_backend_memory_contexts| SELECT name,
     level,
     context_ids,
     total_bytes,
+    total_bytes_including_children,
     total_nblocks,
     free_bytes,
     free_chunks,
     used_bytes
-   FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, parent, type, level, context_ids, 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, context_ids, total_bytes, total_bytes_including_children, 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 af4d82adf4..4bec9a752d 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -65,6 +65,20 @@ where c1.name = 'CacheMemoryContext'
  t
 (1 row)
 
+-- Test whether total_bytes_including_children really sums up to the total
+-- bytes used by all child contexts of CacheMemoryContext.
+with contexts as (
+  select * from pg_backend_memory_contexts
+)
+select min(c1.total_bytes_including_children) = sum(c2.total_bytes)
+from contexts c1, contexts c2
+where c1.name = 'CacheMemoryContext'
+  and c1.context_ids[1] = c2.context_ids[c2.level-c1.level+1];
+ ?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 f0ac59387d..ae5fd1a9fa 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -42,6 +42,16 @@ from contexts c1, contexts c2
 where c1.name = 'CacheMemoryContext'
   and c1.context_ids[1] = c2.context_ids[c2.level-c1.level+1];
 
+-- Test whether total_bytes_including_children really sums up to the total
+-- bytes used by all child contexts of CacheMemoryContext.
+with contexts as (
+  select * from pg_backend_memory_contexts
+)
+select min(c1.total_bytes_including_children) = sum(c2.total_bytes)
+from contexts c1, contexts c2
+where c1.name = 'CacheMemoryContext'
+  and c1.context_ids[1] = c2.context_ids[c2.level-c1.level+1];
+
 -- At introduction, pg_config had 23 entries; it may grow
 select count(*) > 20 as ok from pg_config;
 
-- 
2.34.1

