From 64e24e2b8304619a305c8000b12d825e3b80aae5 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
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 <limits.h>
 #include <ctype.h>
 
+#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

