diff options
Diffstat (limited to 'src/backend/utils/adt')
-rw-r--r-- | src/backend/utils/adt/arrayutils.c | 3 | ||||
-rw-r--r-- | src/backend/utils/adt/int.c | 4 | ||||
-rw-r--r-- | src/backend/utils/adt/int8.c | 1 | ||||
-rw-r--r-- | src/backend/utils/adt/numutils.c | 149 | ||||
-rw-r--r-- | src/backend/utils/adt/varlena.c | 4 |
5 files changed, 155 insertions, 6 deletions
diff --git a/src/backend/utils/adt/arrayutils.c b/src/backend/utils/adt/arrayutils.c index c0d719e98cc..5b98efe76bc 100644 --- a/src/backend/utils/adt/arrayutils.c +++ b/src/backend/utils/adt/arrayutils.c @@ -226,8 +226,7 @@ ArrayGetIntegerTypmods(ArrayType *arr, int *n) result = (int32 *) palloc(*n * sizeof(int32)); for (i = 0; i < *n; i++) - result[i] = pg_atoi(DatumGetCString(elem_values[i]), - sizeof(int32), '\0'); + result[i] = pg_strtoint32(DatumGetCString(elem_values[i])); pfree(elem_values); diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c index 02783d8d6fe..8149dc1369b 100644 --- a/src/backend/utils/adt/int.c +++ b/src/backend/utils/adt/int.c @@ -60,7 +60,7 @@ int2in(PG_FUNCTION_ARGS) { char *num = PG_GETARG_CSTRING(0); - PG_RETURN_INT16(pg_atoi(num, sizeof(int16), '\0')); + PG_RETURN_INT16(pg_strtoint16(num)); } /* @@ -265,7 +265,7 @@ int4in(PG_FUNCTION_ARGS) { char *num = PG_GETARG_CSTRING(0); - PG_RETURN_INT32(pg_atoi(num, sizeof(int32), '\0')); + PG_RETURN_INT32(pg_strtoint32(num)); } /* diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c index 49f32a8b3dd..3c595e800a4 100644 --- a/src/backend/utils/adt/int8.c +++ b/src/backend/utils/adt/int8.c @@ -101,6 +101,7 @@ scanint8(const char *str, bool errorOK, int64 *result) if (!neg) { + /* could fail if input is most negative number */ if (unlikely(tmp == PG_INT64_MIN)) goto out_of_range; tmp = -tmp; diff --git a/src/backend/utils/adt/numutils.c b/src/backend/utils/adt/numutils.c index fb46f692e3a..49055d519cb 100644 --- a/src/backend/utils/adt/numutils.c +++ b/src/backend/utils/adt/numutils.c @@ -18,6 +18,7 @@ #include <limits.h> #include <ctype.h> +#include "common/int.h" #include "utils/builtins.h" /* @@ -109,6 +110,154 @@ pg_atoi(const char *s, int size, int c) } /* + * Convert input string to a signed 16 bit integer. + * + * Allows any number of leading or trailing whitespace characters. Will throw + * ereport() upon bad input format or overflow. + * + * NB: Accumulate input as a negative number, to deal with two's complement + * representation of the most negative number, which can't be represented as a + * positive number. + */ +int16 +pg_strtoint16(const char *s) +{ + const char *ptr = s; + int16 tmp = 0; + bool neg = false; + + /* skip leading spaces */ + while (likely(*ptr) && isspace((unsigned char) *ptr)) + ptr++; + + /* handle sign */ + if (*ptr == '-') + { + ptr++; + neg = true; + } + else if (*ptr == '+') + ptr++; + + /* require at least one digit */ + if (unlikely(!isdigit((unsigned char) *ptr))) + goto invalid_syntax; + + /* process digits */ + while (*ptr && isdigit((unsigned char) *ptr)) + { + int8 digit = (*ptr++ - '0'); + + if (unlikely(pg_mul_s16_overflow(tmp, 10, &tmp)) || + unlikely(pg_sub_s16_overflow(tmp, digit, &tmp))) + goto out_of_range; + } + + /* allow trailing whitespace, but not other trailing chars */ + while (*ptr != '\0' && isspace((unsigned char) *ptr)) + ptr++; + + if (unlikely(*ptr != '\0')) + goto invalid_syntax; + + if (!neg) + { + /* could fail if input is most negative number */ + if (unlikely(tmp == PG_INT16_MIN)) + goto out_of_range; + tmp = -tmp; + } + + return tmp; + +out_of_range: + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value \"%s\" is out of range for type %s", + s, "smallint"))); + +invalid_syntax: + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type %s: \"%s\"", + "smallint", s))); +} + +/* + * Convert input string to a signed 32 bit integer. + * + * Allows any number of leading or trailing whitespace characters. Will throw + * ereport() upon bad input format or overflow. + * + * NB: Accumulate input as a negative number, to deal with two's complement + * representation of the most negative number, which can't be represented as a + * positive number. + */ +int32 +pg_strtoint32(const char *s) +{ + const char *ptr = s; + int32 tmp = 0; + bool neg = false; + + /* skip leading spaces */ + while (likely(*ptr) && isspace((unsigned char) *ptr)) + ptr++; + + /* handle sign */ + if (*ptr == '-') + { + ptr++; + neg = true; + } + else if (*ptr == '+') + ptr++; + + /* require at least one digit */ + if (unlikely(!isdigit((unsigned char) *ptr))) + goto invalid_syntax; + + /* process digits */ + while (*ptr && isdigit((unsigned char) *ptr)) + { + int8 digit = (*ptr++ - '0'); + + if (unlikely(pg_mul_s32_overflow(tmp, 10, &tmp)) || + unlikely(pg_sub_s32_overflow(tmp, digit, &tmp))) + goto out_of_range; + } + + /* allow trailing whitespace, but not other trailing chars */ + while (*ptr != '\0' && isspace((unsigned char) *ptr)) + ptr++; + + if (unlikely(*ptr != '\0')) + goto invalid_syntax; + + if (!neg) + { + /* could fail if input is most negative number */ + if (unlikely(tmp == PG_INT32_MIN)) + goto out_of_range; + tmp = -tmp; + } + + return tmp; + +out_of_range: + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value \"%s\" is out of range for type %s", + s, "integer"))); + +invalid_syntax: + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type %s: \"%s\"", + "integer", s))); +} + +/* * pg_itoa: converts a signed 16-bit integer to its string representation * * Caller must ensure that 'a' points to enough memory to hold the result diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c index e8500b274dc..31eaa92c3b7 100644 --- a/src/backend/utils/adt/varlena.c +++ b/src/backend/utils/adt/varlena.c @@ -5155,8 +5155,8 @@ text_format(PG_FUNCTION_ARGS) str = OutputFunctionCall(&typoutputinfo_width, value); - /* pg_atoi will complain about bad data or overflow */ - width = pg_atoi(str, sizeof(int), '\0'); + /* pg_strtoint32 will complain about bad data or overflow */ + width = pg_strtoint32(str); pfree(str); } |