From 64e24e2b8304619a305c8000b12d825e3b80aae5 Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Fri, 8 Dec 2017 13:31:15 -0800 Subject: [PATCH] Hand code string to int32 conversion for performance. --- src/backend/utils/adt/int.c | 2 +- src/backend/utils/adt/numutils.c | 90 ++++++++++++++++++++++++++++++++++++++++ src/include/utils/builtins.h | 1 + 3 files changed, 92 insertions(+), 1 deletion(-) diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c index 4cd8960b3fc..8af4f8f3f7a 100644 --- a/src/backend/utils/adt/int.c +++ b/src/backend/utils/adt/int.c @@ -267,7 +267,7 @@ int4in(PG_FUNCTION_ARGS) { char *num = PG_GETARG_CSTRING(0); - PG_RETURN_INT32(pg_atoi(num, sizeof(int32), '\0')); + PG_RETURN_INT32(pg_strto32(num)); } /* diff --git a/src/backend/utils/adt/numutils.c b/src/backend/utils/adt/numutils.c index 244904ea940..f2281f86dae 100644 --- a/src/backend/utils/adt/numutils.c +++ b/src/backend/utils/adt/numutils.c @@ -18,6 +18,7 @@ #include #include +#include "common/int.h" #include "utils/builtins.h" /* @@ -108,6 +109,95 @@ pg_atoi(const char *s, int size, int c) return (int32) l; } + +/* + * Convert input string to a 32 bit integer. + * + * Allows any number of leading or trailing whitespace characters. This 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_strto32(const char *s) +{ + const char *in = s; + int32 tmp = 0; + bool neg = 0; + + + /* skip leading spaces */ + while (likely(*in) && isspace((unsigned char) *in)) + in++; + + /* handle sign */ + if (*in == '-') + { + in++; + neg = true; + } + else if (*in == '+') + in++; + + /* require at least one digit */ + if (unlikely(!isdigit((unsigned char) *in))) + goto err; + + /* process digits */ + while (true) + { + if (!*in) + goto out; + if (!isdigit((unsigned char) *in)) + goto checkspace; + + /* accumulate input */ + if (unlikely(pg_mul32_overflow(tmp, 10, &tmp)) || + unlikely(pg_sub32_overflow(tmp, *in - '0', &tmp))) + goto overflow; + in++; + } + +checkspace: + /* allow trailing whitespace, but not other trailing chars */ + while (*in != '\0' && isspace((unsigned char) *in)) + in++; + + if (unlikely(*in != '\0')) + goto err; + +out: + /* + * Accumulated input as a negative number, so adjust if that's not what's + * needed. + */ + if (!neg) + { + /* could fail if input is most negative number */ + if (unlikely(tmp == PG_INT32_MIN)) + goto overflow; + + return -tmp; + } + + return tmp; + +overflow: + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value \"%s\" is out of range for type %s", + s, "integer"))); + +err: + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for integer: \"%s\"", + s))); +} + + /* * pg_itoa: converts a signed 16-bit integer to its string representation * diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 762532f6369..fa45c84b752 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -43,6 +43,7 @@ extern int namestrcmp(Name name, const char *str); /* numutils.c */ extern int32 pg_atoi(const char *s, int size, int c); +extern int32 pg_strto32(const char *s); extern void pg_itoa(int16 i, char *a); extern void pg_ltoa(int32 l, char *a); extern void pg_lltoa(int64 ll, char *a); -- 2.14.1.536.g6867272d5b.dirty