diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 94ef2f0..3b25b85 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -14994,7 +14994,20 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup()); pg_size_pretty(bigint) text - Converts a size in bytes into a human-readable format with size units + + Converts a size in bytes expressed as a 64-bit integer into a + human-readable format with size units + + + + + pg_size_pretty(numeric) + + text + + Converts a size in bytes expressed as a numeric value into a + human-readable format with size units + diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c index 26a8c01..fd19de7 100644 --- a/src/backend/utils/adt/dbsize.c +++ b/src/backend/utils/adt/dbsize.c @@ -24,6 +24,7 @@ #include "storage/fd.h" #include "utils/acl.h" #include "utils/builtins.h" +#include "utils/numeric.h" #include "utils/rel.h" #include "utils/relmapper.h" #include "utils/syscache.h" @@ -550,6 +551,137 @@ pg_size_pretty(PG_FUNCTION_ARGS) PG_RETURN_TEXT_P(cstring_to_text(buf)); } +static char * +numeric_to_cstring(Numeric n) +{ + Datum d = NumericGetDatum(n); + return DatumGetCString(DirectFunctionCall1(numeric_out, d)); +} + +static Numeric +int64_to_numeric(int64 v) +{ + Datum d = Int64GetDatum(v); + return DatumGetNumeric(DirectFunctionCall1(int8_numeric, d)); +} + +static bool +numeric_is_less(Numeric a, Numeric b) +{ + Datum da = NumericGetDatum(a); + Datum db = NumericGetDatum(b); + + return DatumGetBool(DirectFunctionCall2(numeric_lt, da, db)); +} + +static Numeric +numeric_plus_one_over_two(Numeric n) +{ + Datum d = NumericGetDatum(n); + Datum one; + Datum two; + Datum result; + + one = DirectFunctionCall1(int8_numeric, Int64GetDatum(1)); + two = DirectFunctionCall1(int8_numeric, Int64GetDatum(2)); + result = DirectFunctionCall2(numeric_add, d, one); + result = DirectFunctionCall2(numeric_div_trunc, result, two); + return DatumGetNumeric(result); +} + +static Numeric +numeric_shift_right(Numeric n, unsigned count) +{ + Datum d = NumericGetDatum(n); + Datum divisor_int64; + Datum divisor_numeric; + Datum result; + + divisor_int64 = Int64GetDatum((int64) (1 << count)); + divisor_numeric = DirectFunctionCall1(int8_numeric, divisor_int64); + result = DirectFunctionCall2(numeric_div_trunc, d, divisor_numeric); + return DatumGetNumeric(result); +} + +Datum +pg_size_pretty_numeric(PG_FUNCTION_ARGS) +{ + Numeric size = PG_GETARG_NUMERIC(0); + Numeric limit, + limit2; + char *buf, + *result; + + limit = int64_to_numeric(10 * 1024); + limit2 = int64_to_numeric(10 * 1024 * 2 - 1); + + if (numeric_is_less(size, limit)) + { + buf = numeric_to_cstring(size); + result = palloc(strlen(buf) + 7); + strcpy(result, buf); + strcat(result, " bytes"); + } + else + { + /* keep one extra bit for rounding */ + /* size >>= 9 */ + size = numeric_shift_right(size, 9); + + if (numeric_is_less(size, limit2)) + { + /* size = (size + 1) / 2 */ + size = numeric_plus_one_over_two(size); + buf = numeric_to_cstring(size); + result = palloc(strlen(buf) + 4); + strcpy(result, buf); + strcat(result, " kB"); + } + else + { + /* size >>= 10 */ + size = numeric_shift_right(size, 10); + if (numeric_is_less(size, limit2)) + { + /* size = (size + 1) / 2 */ + size = numeric_plus_one_over_two(size); + buf = numeric_to_cstring(size); + result = palloc(strlen(buf) + 4); + strcpy(result, buf); + strcat(result, " MB"); + } + else + { + /* size >>= 10 */ + size = numeric_shift_right(size, 10); + + if (numeric_is_less(size, limit2)) + { + /* size = (size + 1) / 2 */ + size = numeric_plus_one_over_two(size); + buf = numeric_to_cstring(size); + result = palloc(strlen(buf) + 4); + strcpy(result, buf); + strcat(result, " GB"); + } + else + { + /* size >>= 10 */ + size = numeric_shift_right(size, 10); + /* size = (size + 1) / 2 */ + size = numeric_plus_one_over_two(size); + buf = numeric_to_cstring(size); + result = palloc(strlen(buf) + 4); + strcpy(result, buf); + strcat(result, " TB"); + } + } + } + } + + PG_RETURN_TEXT_P(cstring_to_text(result)); +} + /* * Get the filenode of a relation * diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 6414b33..4854e20 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -2947,6 +2947,8 @@ DESCR("xlog filename, given an xlog location"); DATA(insert OID = 3165 ( pg_xlog_location_diff PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 1700 "25 25" _null_ _null_ _null_ _null_ pg_xlog_location_diff _null_ _null_ _null_ )); DESCR("difference in bytes, given two xlog locations"); +DATA(insert OID = 3166 ( pg_size_pretty PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 25 "1700" _null_ _null_ _null_ _null_ pg_size_pretty_numeric _null_ _null_ _null_ )); +DESCR("convert a numeric to a human readable text using size units"); DATA(insert OID = 3809 ( pg_export_snapshot PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_export_snapshot _null_ _null_ _null_ )); DESCR("export a snapshot"); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 9fda7ad..312b5e9 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -453,6 +453,7 @@ extern Datum pg_database_size_name(PG_FUNCTION_ARGS); extern Datum pg_relation_size(PG_FUNCTION_ARGS); extern Datum pg_total_relation_size(PG_FUNCTION_ARGS); extern Datum pg_size_pretty(PG_FUNCTION_ARGS); +extern Datum pg_size_pretty_numeric(PG_FUNCTION_ARGS); extern Datum pg_table_size(PG_FUNCTION_ARGS); extern Datum pg_indexes_size(PG_FUNCTION_ARGS); extern Datum pg_relation_filenode(PG_FUNCTION_ARGS);