aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/dbsize.c
diff options
context:
space:
mode:
authorDean Rasheed <dean.a.rasheed@gmail.com>2016-02-20 09:57:27 +0000
committerDean Rasheed <dean.a.rasheed@gmail.com>2016-02-20 09:57:27 +0000
commit53874c5228fe16589a4d01b3e1fab3678e0fd8e3 (patch)
tree7ca2581b3e8731254b62b31fbb2f95a1301b3ef1 /src/backend/utils/adt/dbsize.c
parent091b6055e3e52338850370f17835e833ca66ac55 (diff)
downloadpostgresql-53874c5228fe16589a4d01b3e1fab3678e0fd8e3.tar.gz
postgresql-53874c5228fe16589a4d01b3e1fab3678e0fd8e3.zip
Add pg_size_bytes() to parse human-readable size strings.
This will parse strings in the format produced by pg_size_pretty() and return sizes in bytes. This allows queries to be written with clauses like "pg_total_relation_size(oid) > pg_size_bytes('10 GB')". Author: Pavel Stehule with various improvements by Vitaly Burovoy Discussion: http://www.postgresql.org/message-id/CAFj8pRD-tGoDKnxdYgECzA4On01_uRqPrwF-8LdkSE-6bDHp0w@mail.gmail.com Reviewed-by: Vitaly Burovoy, Oleksandr Shulgin, Kyotaro Horiguchi, Michael Paquier and Robert Haas
Diffstat (limited to 'src/backend/utils/adt/dbsize.c')
-rw-r--r--src/backend/utils/adt/dbsize.c149
1 files changed, 149 insertions, 0 deletions
diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c
index 208469203d8..d1072d9ccd6 100644
--- a/src/backend/utils/adt/dbsize.c
+++ b/src/backend/utils/adt/dbsize.c
@@ -700,6 +700,155 @@ pg_size_pretty_numeric(PG_FUNCTION_ARGS)
}
/*
+ * Convert a human-readable size to a size in bytes
+ */
+Datum
+pg_size_bytes(PG_FUNCTION_ARGS)
+{
+ text *arg = PG_GETARG_TEXT_PP(0);
+ char *str,
+ *strptr,
+ *endptr;
+ char saved_char;
+ Numeric num;
+ int64 result;
+ bool have_digits = false;
+
+ str = text_to_cstring(arg);
+
+ /* Skip leading whitespace */
+ strptr = str;
+ while (isspace((unsigned char) *strptr))
+ strptr++;
+
+ /* Check that we have a valid number and determine where it ends */
+ endptr = strptr;
+
+ /* Part (1): sign */
+ if (*endptr == '-' || *endptr == '+')
+ endptr++;
+
+ /* Part (2): main digit string */
+ if (isdigit((unsigned char) *endptr))
+ {
+ have_digits = true;
+ do
+ endptr++;
+ while (isdigit((unsigned char) *endptr));
+ }
+
+ /* Part (3): optional decimal point and fractional digits */
+ if (*endptr == '.')
+ {
+ endptr++;
+ if (isdigit((unsigned char) *endptr))
+ {
+ have_digits = true;
+ do
+ endptr++;
+ while (isdigit((unsigned char) *endptr));
+ }
+ }
+
+ /* Complain if we don't have a valid number at this point */
+ if (!have_digits)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid size: \"%s\"", str)));
+
+ /* Part (4): optional exponent */
+ if (*endptr == 'e' || *endptr == 'E')
+ {
+ long exponent;
+ char *cp;
+
+ /*
+ * Note we might one day support EB units, so if what follows isn't a
+ * number, just treat it all as a unit to be parsed.
+ */
+ exponent = strtol(endptr + 1, &cp, 10);
+ if (cp > endptr + 1)
+ {
+ if (exponent > NUMERIC_MAX_PRECISION ||
+ exponent < -NUMERIC_MAX_PRECISION)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid size: \"%s\"", str)));
+ endptr = cp;
+ }
+ }
+
+ /*
+ * Parse the number, saving the next character, which may be the first
+ * character of the unit string.
+ */
+ saved_char = *endptr;
+ *endptr = '\0';
+
+ num = DatumGetNumeric(DirectFunctionCall3(numeric_in,
+ CStringGetDatum(strptr),
+ ObjectIdGetDatum(InvalidOid),
+ Int32GetDatum(-1)));
+
+ *endptr = saved_char;
+
+ /* Skip whitespace between number and unit */
+ strptr = endptr;
+ while (isspace((unsigned char) *strptr))
+ strptr++;
+
+ /* Handle possible unit */
+ if (*strptr != '\0')
+ {
+ int64 multiplier = 0;
+
+ /* Trim any trailing whitespace */
+ endptr = str + VARSIZE_ANY_EXHDR(arg) - 1;
+
+ while (isspace((unsigned char) *endptr))
+ endptr--;
+
+ endptr++;
+ *endptr = '\0';
+
+ /* Parse the unit case-insensitively */
+ if (pg_strcasecmp(strptr, "bytes") == 0)
+ multiplier = 1;
+ else if (pg_strcasecmp(strptr, "kb") == 0)
+ multiplier = 1024;
+ else if (pg_strcasecmp(strptr, "mb") == 0)
+ multiplier = 1024 * 1024;
+ else if (pg_strcasecmp(strptr, "gb") == 0)
+ multiplier = 1024 * 1024 * 1024;
+ else if (pg_strcasecmp(strptr, "tb") == 0)
+ multiplier = 1024 * 1024 * 1024 * 1024L;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid size: \"%s\"", text_to_cstring(arg)),
+ errdetail("Invalid size unit: \"%s\".", strptr),
+ errhint("Valid units are \"bytes\", \"kB\", \"MB\", \"GB\", and \"TB\".")));
+
+ if (multiplier > 1)
+ {
+ Numeric mul_num;
+
+ mul_num = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
+ Int64GetDatum(multiplier)));
+
+ num = DatumGetNumeric(DirectFunctionCall2(numeric_mul,
+ NumericGetDatum(mul_num),
+ NumericGetDatum(num)));
+ }
+ }
+
+ result = DatumGetInt64(DirectFunctionCall1(numeric_int8,
+ NumericGetDatum(num)));
+
+ PG_RETURN_INT64(result);
+}
+
+/*
* Get the filenode of a relation
*
* This is expected to be used in queries like