From e1d250ba19b13fb6858125b68adbbd6a78ed4393 Mon Sep 17 00:00:00 2001
From: David Christensen <david.christensen@crunchydata.com>
Date: Fri, 2 Apr 2021 14:35:31 -0500
Subject: [PATCH 1/2] Expand the units that pg_size_pretty(numeric) knows about

Rework the logic slightly to allow a lookup table-based approach for
displaying units.  Add units up to yottabytes, with easy expansion if
this ever needs to grow.
---
 doc/src/sgml/func.sgml               |  2 +-
 src/backend/utils/adt/dbsize.c       | 43 ++++++++++--------
 src/test/regress/expected/dbsize.out | 67 +++++++++++++++++++---------
 src/test/regress/sql/dbsize.sql      | 27 ++++++++---
 4 files changed, 94 insertions(+), 45 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 08b07f561e..f6739f150c 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -26371,7 +26371,7 @@ postgres=# SELECT * FROM pg_walfile_name_offset(pg_stop_backup());
        </para>
        <para>
         Converts a size in bytes into a more easily human-readable format with
-        size units (bytes, kB, MB, GB or TB as appropriate).  Note that the
+        size units (bytes, kB, MB, GB, TB, PB, EB, ZB, or YB as appropriate).  Note that the
         units are powers of 2 rather than powers of 10, so 1kB is 1024 bytes,
         1MB is 1024<superscript>2</superscript> = 1048576 bytes, and so on.
        </para></entry>
diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c
index 3c70bb5943..f25ec5c1af 100644
--- a/src/backend/utils/adt/dbsize.c
+++ b/src/backend/utils/adt/dbsize.c
@@ -638,7 +638,7 @@ pg_size_pretty_numeric(PG_FUNCTION_ARGS)
 	Numeric		size = PG_GETARG_NUMERIC(0);
 	Numeric		limit,
 				limit2;
-	char	   *result;
+	char	   *result = NULL;
 
 	limit = int64_to_numeric(10 * 1024);
 	limit2 = int64_to_numeric(10 * 1024 * 2 - 1);
@@ -660,31 +660,36 @@ pg_size_pretty_numeric(PG_FUNCTION_ARGS)
 		}
 		else
 		{
-			/* size >>= 10 */
-			size = numeric_shift_right(size, 10);
-			if (numeric_is_less(numeric_absolute(size), limit2))
-			{
-				size = numeric_half_rounded(size);
-				result = psprintf("%s MB", numeric_to_cstring(size));
-			}
-			else
-			{
+			int idx, max_iter = 6; /* highest index of table_below */
+			char *output_formats[] = {
+				"%s MB",
+				"%s GB",
+				"%s TB",
+				"%s PB",
+				"%s EB",
+				"%s ZB",
+				"%s YB"
+			};
+
+			for (idx = 0; idx < max_iter; idx++) {
 				/* size >>= 10 */
 				size = numeric_shift_right(size, 10);
-
 				if (numeric_is_less(numeric_absolute(size), limit2))
 				{
 					size = numeric_half_rounded(size);
-					result = psprintf("%s GB", numeric_to_cstring(size));
-				}
-				else
-				{
-					/* size >>= 10 */
-					size = numeric_shift_right(size, 10);
-					size = numeric_half_rounded(size);
-					result = psprintf("%s TB", numeric_to_cstring(size));
+					result = psprintf(output_formats[idx], numeric_to_cstring(size));
+					break;
 				}
 			}
+
+			if (!result) {
+				/* this uses the last format in the table above for anything else */
+
+				/* size >>= 10 */
+				size = numeric_shift_right(size, 10);
+				size = numeric_half_rounded(size);
+				result = psprintf(output_formats[max_iter], numeric_to_cstring(size));
+			}
 		}
 	}
 
diff --git a/src/test/regress/expected/dbsize.out b/src/test/regress/expected/dbsize.out
index e901a2c92a..b93d40fb02 100644
--- a/src/test/regress/expected/dbsize.out
+++ b/src/test/regress/expected/dbsize.out
@@ -13,27 +13,54 @@ SELECT size, pg_size_pretty(size), pg_size_pretty(-1 * size) FROM
 (6 rows)
 
 SELECT size, pg_size_pretty(size), pg_size_pretty(-1 * size) FROM
-    (VALUES (10::numeric), (1000::numeric), (1000000::numeric),
-            (1000000000::numeric), (1000000000000::numeric),
+    (VALUES (10::numeric),
+            (1000::numeric),
+            (1000000::numeric),
+            (1000000000::numeric),
+            (1000000000000::numeric),
             (1000000000000000::numeric),
-            (10.5::numeric), (1000.5::numeric), (1000000.5::numeric),
-            (1000000000.5::numeric), (1000000000000.5::numeric),
-            (1000000000000000.5::numeric)) x(size);
-        size        | pg_size_pretty | pg_size_pretty 
---------------------+----------------+----------------
-                 10 | 10 bytes       | -10 bytes
-               1000 | 1000 bytes     | -1000 bytes
-            1000000 | 977 kB         | -977 kB
-         1000000000 | 954 MB         | -954 MB
-      1000000000000 | 931 GB         | -931 GB
-   1000000000000000 | 909 TB         | -909 TB
-               10.5 | 10.5 bytes     | -10.5 bytes
-             1000.5 | 1000.5 bytes   | -1000.5 bytes
-          1000000.5 | 977 kB         | -977 kB
-       1000000000.5 | 954 MB         | -954 MB
-    1000000000000.5 | 931 GB         | -931 GB
- 1000000000000000.5 | 909 TB         | -909 TB
-(12 rows)
+            (1000000000000000000::numeric),
+            (1000000000000000000000::numeric),
+            (1000000000000000000000000::numeric),
+            (1000000000000000000000000000::numeric),
+            (1000000000000000000000000000000::numeric),
+            (10.5::numeric),
+            (1000.5::numeric),
+            (1000000.5::numeric),
+            (1000000000.5::numeric),
+            (1000000000000.5::numeric),
+            (1000000000000000.5::numeric),
+            (1000000000000000000.5::numeric),
+            (1000000000000000000000.5::numeric),
+            (1000000000000000000000000.5::numeric),
+            (1000000000000000000000000000.5::numeric),
+            (1000000000000000000000000000000.5::numeric)
+            ) x(size);
+               size                | pg_size_pretty | pg_size_pretty 
+-----------------------------------+----------------+----------------
+                                10 | 10 bytes       | -10 bytes
+                              1000 | 1000 bytes     | -1000 bytes
+                           1000000 | 977 kB         | -977 kB
+                        1000000000 | 954 MB         | -954 MB
+                     1000000000000 | 931 GB         | -931 GB
+                  1000000000000000 | 909 TB         | -909 TB
+               1000000000000000000 | 888 PB         | -888 PB
+            1000000000000000000000 | 867 EB         | -867 EB
+         1000000000000000000000000 | 847 ZB         | -847 ZB
+      1000000000000000000000000000 | 827 YB         | -827 YB
+   1000000000000000000000000000000 | 827181 YB      | -827181 YB
+                              10.5 | 10.5 bytes     | -10.5 bytes
+                            1000.5 | 1000.5 bytes   | -1000.5 bytes
+                         1000000.5 | 977 kB         | -977 kB
+                      1000000000.5 | 954 MB         | -954 MB
+                   1000000000000.5 | 931 GB         | -931 GB
+                1000000000000000.5 | 909 TB         | -909 TB
+             1000000000000000000.5 | 888 PB         | -888 PB
+          1000000000000000000000.5 | 867 EB         | -867 EB
+       1000000000000000000000000.5 | 847 ZB         | -847 ZB
+    1000000000000000000000000000.5 | 827 YB         | -827 YB
+ 1000000000000000000000000000000.5 | 827181 YB      | -827181 YB
+(22 rows)
 
 SELECT size, pg_size_bytes(size) FROM
     (VALUES ('1'), ('123bytes'), ('1kB'), ('1MB'), (' 1 GB'), ('1.5 GB '),
diff --git a/src/test/regress/sql/dbsize.sql b/src/test/regress/sql/dbsize.sql
index d10a4d7f68..a97dacbf7b 100644
--- a/src/test/regress/sql/dbsize.sql
+++ b/src/test/regress/sql/dbsize.sql
@@ -4,12 +4,29 @@ SELECT size, pg_size_pretty(size), pg_size_pretty(-1 * size) FROM
             (1000000000000000::bigint)) x(size);
 
 SELECT size, pg_size_pretty(size), pg_size_pretty(-1 * size) FROM
-    (VALUES (10::numeric), (1000::numeric), (1000000::numeric),
-            (1000000000::numeric), (1000000000000::numeric),
+    (VALUES (10::numeric),
+            (1000::numeric),
+            (1000000::numeric),
+            (1000000000::numeric),
+            (1000000000000::numeric),
             (1000000000000000::numeric),
-            (10.5::numeric), (1000.5::numeric), (1000000.5::numeric),
-            (1000000000.5::numeric), (1000000000000.5::numeric),
-            (1000000000000000.5::numeric)) x(size);
+            (1000000000000000000::numeric),
+            (1000000000000000000000::numeric),
+            (1000000000000000000000000::numeric),
+            (1000000000000000000000000000::numeric),
+            (1000000000000000000000000000000::numeric),
+            (10.5::numeric),
+            (1000.5::numeric),
+            (1000000.5::numeric),
+            (1000000000.5::numeric),
+            (1000000000000.5::numeric),
+            (1000000000000000.5::numeric),
+            (1000000000000000000.5::numeric),
+            (1000000000000000000000.5::numeric),
+            (1000000000000000000000000.5::numeric),
+            (1000000000000000000000000000.5::numeric),
+            (1000000000000000000000000000000.5::numeric)
+            ) x(size);
 
 SELECT size, pg_size_bytes(size) FROM
     (VALUES ('1'), ('123bytes'), ('1kB'), ('1MB'), (' 1 GB'), ('1.5 GB '),
-- 
2.30.1 (Apple Git-130)

