Skip to content

Commit 56ff8b2

Browse files
committed
Use a lookup table for units in pg_size_pretty and pg_size_bytes
We've grown 2 versions of pg_size_pretty over the years, one for BIGINT and one for NUMERIC. Both should output the same, but keeping them in sync is harder than needed due to neither function sharing a source of truth about which units to use and how to transition to the next largest unit. Here we add a static array which defines the units that we recognize and have both pg_size_pretty and pg_size_pretty_numeric use it. This will make adding any units in the future a very simple task. The table contains all information required to allow us to also modify pg_size_bytes to use the lookup table, so adjust that too. There are no behavioral changes here. Author: David Rowley Reviewed-by: Dean Rasheed, Tom Lane, David Christensen Discussion: https://postgr.es/m/CAApHDvru1F7qsEVL-iOHeezJ+5WVxXnyD_Jo9nht+Eh85ekK-Q@mail.gmail.com
1 parent 55fe609 commit 56ff8b2

File tree

1 file changed

+80
-90
lines changed

1 file changed

+80
-90
lines changed

src/backend/utils/adt/dbsize.c

Lines changed: 80 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,27 @@
3434
/* Divide by two and round away from zero */
3535
#define half_rounded(x) (((x) + ((x) < 0 ? -1 : 1)) / 2)
3636

37+
/* Units used in pg_size_pretty functions. All units must be powers of 2 */
38+
struct size_pretty_unit
39+
{
40+
const char *name; /* bytes, kB, MB, GB etc */
41+
uint32 limit; /* upper limit, prior to half rounding after
42+
* converting to this unit. */
43+
bool round; /* do half rounding for this unit */
44+
uint8 unitbits; /* (1 << unitbits) bytes to make 1 of this
45+
* unit */
46+
};
47+
48+
/* When adding units here also update the error message in pg_size_bytes */
49+
static const struct size_pretty_unit size_pretty_units[] = {
50+
{"bytes", 10 * 1024, false, 0},
51+
{"kB", 20 * 1024 - 1, true, 10},
52+
{"MB", 20 * 1024 - 1, true, 20},
53+
{"GB", 20 * 1024 - 1, true, 30},
54+
{"TB", 20 * 1024 - 1, true, 40},
55+
{NULL, 0, false, 0}
56+
};
57+
3758
/* Return physical size of directory contents, or 0 if dir doesn't exist */
3859
static int64
3960
db_dir_size(const char *path)
@@ -535,41 +556,34 @@ pg_size_pretty(PG_FUNCTION_ARGS)
535556
{
536557
int64 size = PG_GETARG_INT64(0);
537558
char buf[64];
538-
int64 limit = 10 * 1024;
539-
int64 limit2 = limit * 2 - 1;
559+
const struct size_pretty_unit *unit;
540560

541-
if (Abs(size) < limit)
542-
snprintf(buf, sizeof(buf), INT64_FORMAT " bytes", size);
543-
else
561+
for (unit = size_pretty_units; unit->name != NULL; unit++)
544562
{
545-
/*
546-
* We use divide instead of bit shifting so that behavior matches for
547-
* both positive and negative size values.
548-
*/
549-
size /= (1 << 9); /* keep one extra bit for rounding */
550-
if (Abs(size) < limit2)
551-
snprintf(buf, sizeof(buf), INT64_FORMAT " kB",
552-
half_rounded(size));
553-
else
563+
uint8 bits;
564+
565+
/* use this unit if there are no more units or we're below the limit */
566+
if (unit[1].name == NULL || Abs(size) < unit->limit)
554567
{
555-
size /= (1 << 10);
556-
if (Abs(size) < limit2)
557-
snprintf(buf, sizeof(buf), INT64_FORMAT " MB",
558-
half_rounded(size));
559-
else
560-
{
561-
size /= (1 << 10);
562-
if (Abs(size) < limit2)
563-
snprintf(buf, sizeof(buf), INT64_FORMAT " GB",
564-
half_rounded(size));
565-
else
566-
{
567-
size /= (1 << 10);
568-
snprintf(buf, sizeof(buf), INT64_FORMAT " TB",
569-
half_rounded(size));
570-
}
571-
}
568+
if (unit->round)
569+
size = half_rounded(size);
570+
571+
snprintf(buf, sizeof(buf), INT64_FORMAT " %s", size, unit->name);
572+
break;
572573
}
574+
575+
/*
576+
* Determine the number of bits to use to build the divisor. We may
577+
* need to use 1 bit less than the difference between this and the
578+
* next unit if the next unit uses half rounding. Or we may need to
579+
* shift an extra bit if this unit uses half rounding and the next one
580+
* does not. We use division rather than shifting right by this
581+
* number of bits to ensure positive and negative values are rounded
582+
* in the same way.
583+
*/
584+
bits = (unit[1].unitbits - unit->unitbits - (unit[1].round == true)
585+
+ (unit->round == true));
586+
size /= ((int64) 1) << bits;
573587
}
574588

575589
PG_RETURN_TEXT_P(cstring_to_text(buf));
@@ -640,57 +654,35 @@ Datum
640654
pg_size_pretty_numeric(PG_FUNCTION_ARGS)
641655
{
642656
Numeric size = PG_GETARG_NUMERIC(0);
643-
Numeric limit,
644-
limit2;
645-
char *result;
646-
647-
limit = int64_to_numeric(10 * 1024);
648-
limit2 = int64_to_numeric(10 * 1024 * 2 - 1);
657+
char *result = NULL;
658+
const struct size_pretty_unit *unit;
649659

650-
if (numeric_is_less(numeric_absolute(size), limit))
660+
for (unit = size_pretty_units; unit->name != NULL; unit++)
651661
{
652-
result = psprintf("%s bytes", numeric_to_cstring(size));
653-
}
654-
else
655-
{
656-
/* keep one extra bit for rounding */
657-
/* size /= (1 << 9) */
658-
size = numeric_truncated_divide(size, 1 << 9);
662+
unsigned int shiftby;
659663

660-
if (numeric_is_less(numeric_absolute(size), limit2))
664+
/* use this unit if there are no more units or we're below the limit */
665+
if (unit[1].name == NULL ||
666+
numeric_is_less(numeric_absolute(size),
667+
int64_to_numeric(unit->limit)))
661668
{
662-
size = numeric_half_rounded(size);
663-
result = psprintf("%s kB", numeric_to_cstring(size));
664-
}
665-
else
666-
{
667-
/* size /= (1 << 10) */
668-
size = numeric_truncated_divide(size, 1 << 10);
669-
670-
if (numeric_is_less(numeric_absolute(size), limit2))
671-
{
669+
if (unit->round)
672670
size = numeric_half_rounded(size);
673-
result = psprintf("%s MB", numeric_to_cstring(size));
674-
}
675-
else
676-
{
677-
/* size /= (1 << 10) */
678-
size = numeric_truncated_divide(size, 1 << 10);
679-
680-
if (numeric_is_less(numeric_absolute(size), limit2))
681-
{
682-
size = numeric_half_rounded(size);
683-
result = psprintf("%s GB", numeric_to_cstring(size));
684-
}
685-
else
686-
{
687-
/* size /= (1 << 10) */
688-
size = numeric_truncated_divide(size, 1 << 10);
689-
size = numeric_half_rounded(size);
690-
result = psprintf("%s TB", numeric_to_cstring(size));
691-
}
692-
}
671+
672+
result = psprintf("%s %s", numeric_to_cstring(size), unit->name);
673+
break;
693674
}
675+
676+
/*
677+
* Determine the number of bits to use to build the divisor. We may
678+
* need to use 1 bit less than the difference between this and the
679+
* next unit if the next unit uses half rounding. Or we may need to
680+
* shift an extra bit if this unit uses half rounding and the next one
681+
* does not.
682+
*/
683+
shiftby = (unit[1].unitbits - unit->unitbits - (unit[1].round == true)
684+
+ (unit->round == true));
685+
size = numeric_truncated_divide(size, ((int64) 1) << shiftby);
694686
}
695687

696688
PG_RETURN_TEXT_P(cstring_to_text(result));
@@ -791,6 +783,7 @@ pg_size_bytes(PG_FUNCTION_ARGS)
791783
/* Handle possible unit */
792784
if (*strptr != '\0')
793785
{
786+
const struct size_pretty_unit *unit;
794787
int64 multiplier = 0;
795788

796789
/* Trim any trailing whitespace */
@@ -802,21 +795,18 @@ pg_size_bytes(PG_FUNCTION_ARGS)
802795
endptr++;
803796
*endptr = '\0';
804797

805-
/* Parse the unit case-insensitively */
806-
if (pg_strcasecmp(strptr, "bytes") == 0)
807-
multiplier = (int64) 1;
808-
else if (pg_strcasecmp(strptr, "kb") == 0)
809-
multiplier = (int64) 1024;
810-
else if (pg_strcasecmp(strptr, "mb") == 0)
811-
multiplier = ((int64) 1024) * 1024;
812-
813-
else if (pg_strcasecmp(strptr, "gb") == 0)
814-
multiplier = ((int64) 1024) * 1024 * 1024;
815-
816-
else if (pg_strcasecmp(strptr, "tb") == 0)
817-
multiplier = ((int64) 1024) * 1024 * 1024 * 1024;
798+
for (unit = size_pretty_units; unit->name != NULL; unit++)
799+
{
800+
/* Parse the unit case-insensitively */
801+
if (pg_strcasecmp(strptr, unit->name) == 0)
802+
{
803+
multiplier = ((int64) 1) << unit->unitbits;
804+
break;
805+
}
806+
}
818807

819-
else
808+
/* Verify we found a valid unit in the loop above */
809+
if (unit->name == NULL)
820810
ereport(ERROR,
821811
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
822812
errmsg("invalid size: \"%s\"", text_to_cstring(arg)),

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy