From 29dbf4ad5bfeba14c8de931eee10ff235e0fd131 Mon Sep 17 00:00:00 2001
From: Georgios Kokolatos <gkokolatos@pm.me>
Date: Mon, 15 Mar 2021 11:29:27 +0000
Subject: [PATCH v5] Attempt to make dbsize a bit more consistent

---
 src/backend/access/table/tableam.c |   8 ++-
 src/backend/utils/adt/dbsize.c     | 102 +++++++++++++----------------
 2 files changed, 51 insertions(+), 59 deletions(-)

diff --git a/src/backend/access/table/tableam.c b/src/backend/access/table/tableam.c
index 5ea5bdd810..af0188177a 100644
--- a/src/backend/access/table/tableam.c
+++ b/src/backend/access/table/tableam.c
@@ -636,10 +636,14 @@ table_block_relation_size(Relation rel, ForkNumber forkNumber)
 	if (forkNumber == InvalidForkNumber)
 	{
 		for (int i = 0; i < MAX_FORKNUM; i++)
-			nblocks += smgrnblocks(rel->rd_smgr, i);
+			nblocks += smgrexists(rel->rd_smgr, i)
+						? smgrnblocks(rel->rd_smgr, i)
+						: 0;
 	}
 	else
-		nblocks = smgrnblocks(rel->rd_smgr, forkNumber);
+		nblocks = smgrexists(rel->rd_smgr, forkNumber)
+					? smgrnblocks(rel->rd_smgr, forkNumber)
+					: 0;
 
 	return nblocks * BLCKSZ;
 }
diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c
index 64cdaa4134..d23aa4e140 100644
--- a/src/backend/utils/adt/dbsize.c
+++ b/src/backend/utils/adt/dbsize.c
@@ -15,6 +15,7 @@
 
 #include "access/htup_details.h"
 #include "access/relation.h"
+#include "access/tableam.h"
 #include "catalog/catalog.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_authid.h"
@@ -34,6 +35,8 @@
 /* Divide by two and round towards positive infinity. */
 #define half_rounded(x)   (((x) + ((x) < 0 ? 0 : 1)) / 2)
 
+static int64 calculate_total_relation_size(Relation rel);
+
 /* Return physical size of directory contents, or 0 if dir doesn't exist */
 static int64
 db_dir_size(const char *path)
@@ -327,84 +330,53 @@ pg_relation_size(PG_FUNCTION_ARGS)
 	if (rel == NULL)
 		PG_RETURN_NULL();
 
-	size = calculate_relation_size(&(rel->rd_node), rel->rd_backend,
+	if (rel->rd_tableam)
+		size = table_relation_size(rel,
 								   forkname_to_number(text_to_cstring(forkName)));
+	else
+		size = calculate_relation_size(&(rel->rd_node), rel->rd_backend,
+									   forkname_to_number(text_to_cstring(forkName)));
 
 	relation_close(rel, AccessShareLock);
 
 	PG_RETURN_INT64(size);
 }
 
-/*
- * Calculate total on-disk size of a TOAST relation, including its indexes.
- * Must not be applied to non-TOAST relations.
- */
-static int64
-calculate_toast_table_size(Oid toastrelid)
-{
-	int64		size = 0;
-	Relation	toastRel;
-	ForkNumber	forkNum;
-	ListCell   *lc;
-	List	   *indexlist;
-
-	toastRel = relation_open(toastrelid, AccessShareLock);
-
-	/* toast heap size, including FSM and VM size */
-	for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
-		size += calculate_relation_size(&(toastRel->rd_node),
-										toastRel->rd_backend, forkNum);
-
-	/* toast index size, including FSM and VM size */
-	indexlist = RelationGetIndexList(toastRel);
-
-	/* Size is calculated using all the indexes available */
-	foreach(lc, indexlist)
-	{
-		Relation	toastIdxRel;
-
-		toastIdxRel = relation_open(lfirst_oid(lc),
-									AccessShareLock);
-		for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
-			size += calculate_relation_size(&(toastIdxRel->rd_node),
-											toastIdxRel->rd_backend, forkNum);
-
-		relation_close(toastIdxRel, AccessShareLock);
-	}
-	list_free(indexlist);
-	relation_close(toastRel, AccessShareLock);
-
-	return size;
-}
-
 /*
  * Calculate total on-disk size of a given table,
- * including FSM and VM, plus TOAST table if any.
+ * plus TOAST table if any.
  * Indexes other than the TOAST table's index are not included.
  *
- * Note that this also behaves sanely if applied to an index or toast table;
- * those won't have attached toast tables, but they can have multiple forks.
+ * Note that this also behaves sanely if applied to a toast table.
  */
 static int64
 calculate_table_size(Relation rel)
 {
-	int64		size = 0;
+	uint64		size = 0;
 	ForkNumber	forkNum;
 
 	/*
-	 * heap size, including FSM and VM
+	 * table size, including all implemented forks (e.g. FSM, VM for heap AM)
+	 * excluding TOAST relation
 	 */
 	for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
-		size += calculate_relation_size(&(rel->rd_node), rel->rd_backend,
-										forkNum);
+		size += table_relation_size(rel, forkNum);
 
 	/*
 	 * Size of toast relation
 	 */
 	if (OidIsValid(rel->rd_rel->reltoastrelid))
-		size += calculate_toast_table_size(rel->rd_rel->reltoastrelid);
+	{
+		Relation	toastRel;
 
-	return size;
+		toastRel = relation_open(rel->rd_rel->reltoastrelid, AccessShareLock);
+
+		size += calculate_total_relation_size(toastRel);
+
+		relation_close(toastRel, AccessShareLock);
+	}
+
+	return (int64)size;
 }
 
 /*
@@ -452,14 +424,22 @@ pg_table_size(PG_FUNCTION_ARGS)
 {
 	Oid			relOid = PG_GETARG_OID(0);
 	Relation	rel;
-	int64		size;
+	int64		size = 0;
 
 	rel = try_relation_open(relOid, AccessShareLock);
 
 	if (rel == NULL)
 		PG_RETURN_NULL();
 
-	size = calculate_table_size(rel);
+	if (rel->rd_tableam)
+		size = calculate_table_size(rel);
+	else
+	{
+		for (ForkNumber forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
+			size += calculate_relation_size(&(rel->rd_node),
+											rel->rd_backend,
+											forkNum);
+	}
 
 	relation_close(rel, AccessShareLock);
 
@@ -487,7 +467,7 @@ pg_indexes_size(PG_FUNCTION_ARGS)
 
 /*
  *	Compute the on-disk size of all files for the relation,
- *	including heap data, index data, toast data, FSM, VM.
+ *	including table data, index data, toast data.
  */
 static int64
 calculate_total_relation_size(Relation rel)
@@ -513,14 +493,22 @@ pg_total_relation_size(PG_FUNCTION_ARGS)
 {
 	Oid			relOid = PG_GETARG_OID(0);
 	Relation	rel;
-	int64		size;
+	uint64		size = 0;
 
 	rel = try_relation_open(relOid, AccessShareLock);
 
 	if (rel == NULL)
 		PG_RETURN_NULL();
 
-	size = calculate_total_relation_size(rel);
+	if (rel->rd_rel->relkind == RELKIND_RELATION ||
+		rel->rd_rel->relkind == RELKIND_TOASTVALUE ||
+		rel->rd_rel->relkind == RELKIND_MATVIEW)
+		size = calculate_total_relation_size(rel);
+	else
+	{
+		relation_close(rel, AccessShareLock);
+		PG_RETURN_NULL();
+	}
 
 	relation_close(rel, AccessShareLock);
 
-- 
2.25.1

