aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2016-08-14 15:06:01 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2016-08-14 15:06:01 -0400
commit9389fbd0385776adf3252eb8cfe6e37a640fdff4 (patch)
treeeb973d4986af7113771e01f79b7f12fa5c5e6e5b
parentd6c9e05cb7db64239887fac65b243229594f331d (diff)
downloadpostgresql-9389fbd0385776adf3252eb8cfe6e37a640fdff4.tar.gz
postgresql-9389fbd0385776adf3252eb8cfe6e37a640fdff4.zip
Remove bogus dependencies on NUMERIC_MAX_PRECISION.
NUMERIC_MAX_PRECISION is a purely arbitrary constraint on the precision and scale you can write in a numeric typmod. It might once have had something to do with the allowed range of a typmod-less numeric value, but at least since 9.1 we've allowed, and documented that we allowed, any value that would physically fit in the numeric storage format; which is something over 100000 decimal digits, not 1000. Hence, get rid of numeric_in()'s use of NUMERIC_MAX_PRECISION as a limit on the allowed range of the exponent in scientific-format input. That was especially silly in view of the fact that you can enter larger numbers as long as you don't use 'e' to do it. Just constrain the value enough to avoid localized overflow, and let make_result be the final arbiter of what is too large. Likewise adjust ecpg's equivalent of this code. Also get rid of numeric_recv()'s use of NUMERIC_MAX_PRECISION to limit the number of base-NBASE digits it would accept. That created a dump/restore hazard for binary COPY without doing anything useful; the wire-format limit on number of digits (65535) is about as tight as we would want. In HEAD, also get rid of pg_size_bytes()'s unnecessary intimacy with what the numeric range limit is. That code doesn't exist in the back branches. Per gripe from Aravind Kumar. Back-patch to all supported branches, since they all contain the documentation claim about allowed range of NUMERIC (cf commit cabf5d84b). Discussion: <2895.1471195721@sss.pgh.pa.us>
-rw-r--r--src/backend/utils/adt/dbsize.c14
-rw-r--r--src/backend/utils/adt/numeric.c21
-rw-r--r--src/include/utils/numeric.h5
-rw-r--r--src/interfaces/ecpg/pgtypeslib/numeric.c3
-rw-r--r--src/test/regress/expected/dbsize.out2
5 files changed, 20 insertions, 25 deletions
diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c
index 0776f3bf6cf..0e8a82d6f4d 100644
--- a/src/backend/utils/adt/dbsize.c
+++ b/src/backend/utils/adt/dbsize.c
@@ -759,23 +759,15 @@ pg_size_bytes(PG_FUNCTION_ARGS)
/* 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.
+ * Note we might one day support EB units, so if what follows 'E'
+ * isn't a number, just treat it all as a unit to be parsed.
*/
- exponent = strtol(endptr + 1, &cp, 10);
+ (void) 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;
- }
}
/*
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 2fbdfe07582..620226cea11 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -753,10 +753,6 @@ numeric_recv(PG_FUNCTION_ARGS)
init_var(&value);
len = (uint16) pq_getmsgint(buf, sizeof(uint16));
- if (len < 0 || len > NUMERIC_MAX_PRECISION + NUMERIC_MAX_RESULT_SCALE)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
- errmsg("invalid length in external \"numeric\" value")));
alloc_var(&value, len);
@@ -5456,12 +5452,19 @@ set_var_from_str(const char *str, const char *cp, NumericVar *dest)
errmsg("invalid input syntax for type numeric: \"%s\"",
str)));
cp = endptr;
- if (exponent > NUMERIC_MAX_PRECISION ||
- exponent < -NUMERIC_MAX_PRECISION)
+
+ /*
+ * At this point, dweight and dscale can't be more than about
+ * INT_MAX/2 due to the MaxAllocSize limit on string length, so
+ * constraining the exponent similarly should be enough to prevent
+ * integer overflow in this function. If the value is too large to
+ * fit in storage format, make_result() will complain about it later;
+ * for consistency use the same ereport errcode/text as make_result().
+ */
+ if (exponent >= INT_MAX / 2 || exponent <= -(INT_MAX / 2))
ereport(ERROR,
- (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("invalid input syntax for type numeric: \"%s\"",
- str)));
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value overflows numeric format")));
dweight += (int) exponent;
dscale -= (int) exponent;
if (dscale < 0)
diff --git a/src/include/utils/numeric.h b/src/include/utils/numeric.h
index 7fd5d240c66..6c1808ec717 100644
--- a/src/include/utils/numeric.h
+++ b/src/include/utils/numeric.h
@@ -17,8 +17,9 @@
#include "fmgr.h"
/*
- * Hardcoded precision limit - arbitrary, but must be small enough that
- * dscale values will fit in 14 bits.
+ * Limit on the precision (and hence scale) specifiable in a NUMERIC typmod.
+ * Note that the implementation limit on the length of a numeric value is
+ * much larger --- beware of what you use this for!
*/
#define NUMERIC_MAX_PRECISION 1000
diff --git a/src/interfaces/ecpg/pgtypeslib/numeric.c b/src/interfaces/ecpg/pgtypeslib/numeric.c
index d061616787e..120794550d6 100644
--- a/src/interfaces/ecpg/pgtypeslib/numeric.c
+++ b/src/interfaces/ecpg/pgtypeslib/numeric.c
@@ -263,8 +263,7 @@ set_var_from_str(char *str, char **ptr, numeric *dest)
return -1;
}
(*ptr) = endptr;
- if (exponent > NUMERIC_MAX_PRECISION ||
- exponent < -NUMERIC_MAX_PRECISION)
+ if (exponent >= INT_MAX / 2 || exponent <= -(INT_MAX / 2))
{
errno = PGTYPES_NUM_BAD_NUMERIC;
return -1;
diff --git a/src/test/regress/expected/dbsize.out b/src/test/regress/expected/dbsize.out
index 20d8cb56893..e901a2c92a1 100644
--- a/src/test/regress/expected/dbsize.out
+++ b/src/test/regress/expected/dbsize.out
@@ -119,7 +119,7 @@ ERROR: bigint out of range
SELECT pg_size_bytes('1e100');
ERROR: bigint out of range
SELECT pg_size_bytes('1e1000000000000000000');
-ERROR: invalid size: "1e1000000000000000000"
+ERROR: value overflows numeric format
SELECT pg_size_bytes('1 byte'); -- the singular "byte" is not supported
ERROR: invalid size: "1 byte"
DETAIL: Invalid size unit: "byte".