Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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

+80-90
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)