Fix incorrect return value for pg_size_pretty(bigint)
authorDavid Rowley <drowley@postgresql.org>
Sun, 28 Jul 2024 10:22:52 +0000 (22:22 +1200)
committerDavid Rowley <drowley@postgresql.org>
Sun, 28 Jul 2024 10:22:52 +0000 (22:22 +1200)
pg_size_pretty(bigint) would return the value in bytes rather than PB
for the smallest-most bigint value.  This happened due to an incorrect
assumption that the absolute value of -9223372036854775808 could be
stored inside a signed 64-bit type.

Here we fix that by instead storing that value in an unsigned 64-bit type.

This bug does exist in versions prior to 15 but the code there is
sufficiently different and the bug seems sufficiently non-critical that
it does not seem worth risking backpatching further.

Author: Joseph Koshakow <koshy44@gmail.com>
Discussion: https://postgr.es/m/CAAvxfHdTsMZPWEHUrZ=h3cky9Ccc3Mtx2whUHygY+ABP-mCmUw@mail.gmail.com
Backpatch-through: 15

src/backend/utils/adt/dbsize.c
src/test/regress/expected/dbsize.out
src/test/regress/sql/dbsize.sql

index 25d7110c130a8189cd8289d31358918a302b456b..b2d9cc2792925686f8225d3e2a7952f49504bc1e 100644 (file)
@@ -575,9 +575,13 @@ pg_size_pretty(PG_FUNCTION_ARGS)
    for (unit = size_pretty_units; unit->name != NULL; unit++)
    {
        uint8       bits;
+       uint64      abs_size = size < 0 ? 0 - (uint64) size : (uint64) size;
 
-       /* use this unit if there are no more units or we're below the limit */
-       if (unit[1].name == NULL || i64abs(size) < unit->limit)
+       /*
+        * Use this unit if there are no more units or the absolute size is
+        * below the limit for the current unit.
+        */
+       if (unit[1].name == NULL || abs_size < unit->limit)
        {
            if (unit->round)
                size = half_rounded(size);
index f1121a87aa340d5a1eaca47487f2b554a3a29fe0..97c3bf54be554c35b7b1bea071eb8a538cb8cbc3 100644 (file)
@@ -79,6 +79,14 @@ SELECT size, pg_size_pretty(size), pg_size_pretty(-1 * size) FROM
  11528652096115048448 | 10240 PB       | -10240 PB
 (12 rows)
 
+-- Ensure we get the expected results when passing the extremities of bigint
+SELECT pg_size_pretty('-9223372036854775808'::bigint),
+       pg_size_pretty('9223372036854775807'::bigint);
+ pg_size_pretty | pg_size_pretty 
+----------------+----------------
+ -8192 PB       | 8192 PB
+(1 row)
+
 -- pg_size_bytes() tests
 SELECT size, pg_size_bytes(size) FROM
     (VALUES ('1'), ('123bytes'), ('256 B'), ('1kB'), ('1MB'), (' 1 GB'), ('1.5 GB '),
index b34cf33385ebda61a4fdf4b6fee4b285f241eb31..38b9444400768bbb83984da46eec28f1342477eb 100644 (file)
@@ -27,6 +27,10 @@ SELECT size, pg_size_pretty(size), pg_size_pretty(-1 * size) FROM
             (11258449312612351::numeric), (11258449312612352::numeric),
             (11528652096115048447::numeric), (11528652096115048448::numeric)) x(size);
 
+-- Ensure we get the expected results when passing the extremities of bigint
+SELECT pg_size_pretty('-9223372036854775808'::bigint),
+       pg_size_pretty('9223372036854775807'::bigint);
+
 -- pg_size_bytes() tests
 SELECT size, pg_size_bytes(size) FROM
     (VALUES ('1'), ('123bytes'), ('256 B'), ('1kB'), ('1MB'), (' 1 GB'), ('1.5 GB '),