diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c index 9c39e7d3b3..2fcfef76f9 100644 --- a/src/backend/utils/adt/dbsize.c +++ b/src/backend/utils/adt/dbsize.c @@ -34,6 +34,30 @@ /* Divide by two and round towards positive infinity. */ #define half_rounded(x) (((x) + ((x) < 0 ? 0 : 1)) / 2) +/* + * Units used in pg_size_pretty functions + */ +struct size_pretty_unit { + char *unit; /* e.g bytes, kB, MB etc */ + uint64 limit; /* upper limit after reducing by shiftright + * bits from previous units */ + uint8 shiftRight; /* number of bits to shift right by after + * limit is exceeded */ + uint8 multishift; /* how many bits to shift "1" left by to make + * one of these units. */ + bool doHalfRound; /* perform halfround operation */ +}; + +/* When adding units here also update the error message in pg_size_bytes */ +static const struct size_pretty_unit size_pretty_units[] = { + {"bytes", 10 * 1024, 9, 0, false}, /* keep one extra bit for rounding */ + {"kB", 20 * 1024 - 1, 10, 10, true}, + {"MB", 20 * 1024 - 1, 10, 20, true}, + {"GB", 20 * 1024 - 1, 10, 30, true}, + {"TB", 20 * 1024 - 1, 10, 40, true}, + {NULL, 0, 0, 0, false} +}; + /* Return physical size of directory contents, or 0 if dir doesn't exist */ static int64 db_dir_size(const char *path) @@ -535,37 +559,30 @@ pg_size_pretty(PG_FUNCTION_ARGS) { int64 size = PG_GETARG_INT64(0); char buf[64]; - int64 limit = 10 * 1024; - int64 limit2 = limit * 2 - 1; + uint32 i; - if (Abs(size) < limit) - snprintf(buf, sizeof(buf), INT64_FORMAT " bytes", size); - else + for (i = 0; size_pretty_units[i].unit != NULL; i++) { - size >>= 9; /* keep one extra bit for rounding */ - if (Abs(size) < limit2) - snprintf(buf, sizeof(buf), INT64_FORMAT " kB", - half_rounded(size)); - else + /* + * Use this set of units if the number is under the limit or there + * is no other unit to use. + */ + if (Abs(size) < size_pretty_units[i].limit || + size_pretty_units[i + 1].unit == NULL) { - size >>= 10; - if (Abs(size) < limit2) - snprintf(buf, sizeof(buf), INT64_FORMAT " MB", - half_rounded(size)); - else - { - size >>= 10; - if (Abs(size) < limit2) - snprintf(buf, sizeof(buf), INT64_FORMAT " GB", - half_rounded(size)); - else - { - size >>= 10; - snprintf(buf, sizeof(buf), INT64_FORMAT " TB", - half_rounded(size)); - } - } + if (size_pretty_units[i].doHalfRound) + size = half_rounded(size); + + snprintf(buf, sizeof(buf), INT64_FORMAT " %s", size, + size_pretty_units[i].unit); + break; } + + /* + * limit for the current unit has been exceeded. Shift right by the + * specified number of bits and try the next unit + */ + size >>= size_pretty_units[i].shiftRight; } PG_RETURN_TEXT_P(cstring_to_text(buf)); @@ -636,56 +653,34 @@ Datum pg_size_pretty_numeric(PG_FUNCTION_ARGS) { Numeric size = PG_GETARG_NUMERIC(0); - Numeric limit, - limit2; + Numeric limit; char *result; + uint32 i; - limit = int64_to_numeric(10 * 1024); - limit2 = int64_to_numeric(10 * 1024 * 2 - 1); - - if (numeric_is_less(numeric_absolute(size), limit)) + for (i = 0; size_pretty_units[i].unit != NULL; i++) { - result = psprintf("%s bytes", numeric_to_cstring(size)); - } - else - { - /* keep one extra bit for rounding */ - /* size >>= 9 */ - size = numeric_shift_right(size, 9); + limit = int64_to_numeric(size_pretty_units[i].limit); - if (numeric_is_less(numeric_absolute(size), limit2)) - { - size = numeric_half_rounded(size); - result = psprintf("%s kB", numeric_to_cstring(size)); - } - else + /* + * Use this set of units if the number is under the limit or there + * is no other unit to use. + */ + if (numeric_is_less(numeric_absolute(size), limit) || + size_pretty_units[i + 1].unit == NULL) { - /* size >>= 10 */ - size = numeric_shift_right(size, 10); - if (numeric_is_less(numeric_absolute(size), limit2)) - { + if (size_pretty_units[i].doHalfRound) size = numeric_half_rounded(size); - result = psprintf("%s MB", numeric_to_cstring(size)); - } - 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 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("%s %s", numeric_to_cstring(size), + size_pretty_units[i].unit); + break; } + + /* + * limit for the current unit has been exceeded. Shift right by the + * specified number of bits and try the next unit + */ + size = numeric_shift_right(size, size_pretty_units[i].shiftRight); } PG_RETURN_TEXT_P(cstring_to_text(result)); @@ -787,6 +782,7 @@ pg_size_bytes(PG_FUNCTION_ARGS) if (*strptr != '\0') { int64 multiplier = 0; + uint32 i; /* Trim any trailing whitespace */ endptr = str + VARSIZE_ANY_EXHDR(arg) - 1; @@ -797,21 +793,18 @@ pg_size_bytes(PG_FUNCTION_ARGS) endptr++; *endptr = '\0'; - /* Parse the unit case-insensitively */ - if (pg_strcasecmp(strptr, "bytes") == 0) - multiplier = (int64) 1; - else if (pg_strcasecmp(strptr, "kb") == 0) - multiplier = (int64) 1024; - else if (pg_strcasecmp(strptr, "mb") == 0) - multiplier = ((int64) 1024) * 1024; - - else if (pg_strcasecmp(strptr, "gb") == 0) - multiplier = ((int64) 1024) * 1024 * 1024; - - else if (pg_strcasecmp(strptr, "tb") == 0) - multiplier = ((int64) 1024) * 1024 * 1024 * 1024; - - else + for (i = 0; size_pretty_units[i].unit != NULL; i++) + { + /* Parse the unit case-insensitively */ + if (pg_strcasecmp(strptr, size_pretty_units[i].unit) == 0) + { + multiplier = ((int64) 1) << size_pretty_units[i].multishift; + break; + } + } + + /* Verify we found a valid unit in the loop above */ + if (size_pretty_units[i].unit == NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid size: \"%s\"", text_to_cstring(arg)),