Michaël,

I have taken the liberty to optimize the existing int64 function by removing
spurious tests.

Which are?

 - *ptr && WHATEVER(*ptr)
  *ptr is redundant, WHATEVER yields false on '\0', and it costs on each
  char but at the end. It might be debatable in some places, e.g. it is
  likely that there are no spaces in the string, but likely that there are
  more than one digit.

  If you want all/some *ptr added back, no problem.

 - isdigit repeated on if and following while, used if/do-while instead.

I have not created uint64 specific inlined overflow functions.

Why?  There is a comment below ;p

See comment about comment below:-)

If it looks ok, a separate patch could address the 32 & 16 versions.

I am surprised to see a negative diff

Is it? Long duplicate functions are factored out (this was my initial intent), one file is removed…

actually just by doing that (adding the 32 and 16 parts will add much more code of course). At quick glance, I think that this is on the right track. Some comments I have on the top of my mind:

- It would me good to have the unsigned equivalents of
pg_mul_s64_overflow, etc.  These are simple enough,

Hmmm. Have you looked at the fallback cases when the corresponding builtins are not available?

I'm unsure of a reliable way to detect a generic unsigned int overflow without expensive dividing back and having to care about zero…

So I was pretty happy with my two discreet, small and efficient tests.

and per the feedback from Andres they could live in common/int.h.

Could be, however "string.c" already contains a string to int conversion function, so I put them together. Probably this function should be removed in the end, though.

- It is more consistent to use upper-case statuses in the enum
strtoint_status.

I thought of that, but first enum I found used lower case, so it did not seem obvious that pg style was to use upper case. Indeed, some enum constants use upper cases.

 Could it be renamed to pg_strtoint_status?

Sure. I also prefixed the enum constants for consistency.

Attached patch uses a prefix and uppers constants. Waiting for further input about other comments.

--
Fabien.
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 221b47298c..28891ba337 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -79,6 +79,7 @@
 #include "utils/builtins.h"
 #include "utils/hashutils.h"
 #include "utils/memutils.h"
+#include "common/string.h"
 
 PG_MODULE_MAGIC;
 
@@ -1022,7 +1023,7 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
 		/* parse command tag to retrieve the number of affected rows. */
 		if (completionTag &&
 			strncmp(completionTag, "COPY ", 5) == 0)
-			rows = pg_strtouint64(completionTag + 5, NULL, 10);
+			(void) pg_strtouint64(completionTag + 5, &rows);
 		else
 			rows = 0;
 
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 2c0ae395ba..8e75d52b06 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -21,6 +21,7 @@
 #include "catalog/heap.h"
 #include "catalog/pg_type.h"
 #include "commands/trigger.h"
+#include "common/string.h"
 #include "executor/executor.h"
 #include "executor/spi_priv.h"
 #include "miscadmin.h"
@@ -2338,8 +2339,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
 					CreateTableAsStmt *ctastmt = (CreateTableAsStmt *) stmt->utilityStmt;
 
 					if (strncmp(completionTag, "SELECT ", 7) == 0)
-						_SPI_current->processed =
-							pg_strtouint64(completionTag + 7, NULL, 10);
+						(void) pg_strtouint64(completionTag + 7, &_SPI_current->processed);
 					else
 					{
 						/*
@@ -2361,8 +2361,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
 				else if (IsA(stmt->utilityStmt, CopyStmt))
 				{
 					Assert(strncmp(completionTag, "COPY ", 5) == 0);
-					_SPI_current->processed = pg_strtouint64(completionTag + 5,
-															 NULL, 10);
+					(void) pg_strtouint64(completionTag + 5, &_SPI_current->processed);
 				}
 			}
 
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 764e3bb90c..17cde42b4d 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -34,6 +34,7 @@
 
 #include "fmgr.h"
 #include "miscadmin.h"
+#include "common/string.h"
 #include "nodes/extensible.h"
 #include "nodes/parsenodes.h"
 #include "nodes/plannodes.h"
@@ -80,7 +81,7 @@
 #define READ_UINT64_FIELD(fldname) \
 	token = pg_strtok(&length);		/* skip :fldname */ \
 	token = pg_strtok(&length);		/* get field value */ \
-	local_node->fldname = pg_strtouint64(token, NULL, 10)
+	(void) pg_strtouint64(token, &local_node->fldname)
 
 /* Read a long integer field (anything written as ":fldname %ld") */
 #define READ_LONG_FIELD(fldname) \
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index 1baf7ef31f..8af5e083ee 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -25,10 +25,10 @@
 #include "parser/parse_expr.h"
 #include "parser/parse_relation.h"
 #include "utils/builtins.h"
-#include "utils/int8.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
 #include "utils/varbit.h"
+#include "common/string.h"
 
 
 static void pcb_error_callback(void *arg);
@@ -496,7 +496,7 @@ make_const(ParseState *pstate, Value *value, int location)
 
 		case T_Float:
 			/* could be an oversize integer as well as a float ... */
-			if (scanint8(strVal(value), true, &val64))
+			if (pg_strtoint64(strVal(value), &val64) == PG_STRTOINT_OK)
 			{
 				/*
 				 * It might actually fit in int32. Probably only INT_MIN can
diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c
index 9c08757fca..e58dbe2fd0 100644
--- a/src/backend/replication/pgoutput/pgoutput.c
+++ b/src/backend/replication/pgoutput/pgoutput.c
@@ -12,6 +12,7 @@
  */
 #include "postgres.h"
 
+#include "common/string.h"
 #include "catalog/pg_publication.h"
 
 #include "fmgr.h"
@@ -22,7 +23,6 @@
 #include "replication/pgoutput.h"
 
 #include "utils/inval.h"
-#include "utils/int8.h"
 #include "utils/memutils.h"
 #include "utils/syscache.h"
 #include "utils/varlena.h"
@@ -113,7 +113,7 @@ parse_output_parameters(List *options, uint32 *protocol_version,
 						 errmsg("conflicting or redundant options")));
 			protocol_version_given = true;
 
-			if (!scanint8(strVal(defel->arg), true, &parsed))
+			if (unlikely(pg_strtoint64(strVal(defel->arg), &parsed) != PG_STRTOINT_OK))
 				ereport(ERROR,
 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 						 errmsg("invalid proto_version")));
diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c
index 6515fc8ec6..15f3babc05 100644
--- a/src/backend/utils/adt/cash.c
+++ b/src/backend/utils/adt/cash.c
@@ -26,7 +26,6 @@
 #include "libpq/pqformat.h"
 #include "utils/builtins.h"
 #include "utils/cash.h"
-#include "utils/int8.h"
 #include "utils/numeric.h"
 #include "utils/pg_locale.h"
 
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index b3115e4bea..05bbd2a006 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -92,7 +92,6 @@
 #include "utils/datetime.h"
 #include "utils/float.h"
 #include "utils/formatting.h"
-#include "utils/int8.h"
 #include "utils/memutils.h"
 #include "utils/numeric.h"
 #include "utils/pg_locale.h"
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index 0ff9394a2f..0152fc9dbd 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -18,12 +18,12 @@
 #include <math.h>
 
 #include "common/int.h"
+#include "common/string.h"
 #include "funcapi.h"
 #include "libpq/pqformat.h"
 #include "nodes/nodeFuncs.h"
 #include "nodes/supportnodes.h"
 #include "optimizer/optimizer.h"
-#include "utils/int8.h"
 #include "utils/builtins.h"
 
 
@@ -47,87 +47,32 @@ typedef struct
  * Formatting and conversion routines.
  *---------------------------------------------------------*/
 
-/*
- * scanint8 --- try to parse a string into an int8.
- *
- * If errorOK is false, ereport a useful error message if the string is bad.
- * If errorOK is true, just return "false" for bad input.
- */
-bool
-scanint8(const char *str, bool errorOK, int64 *result)
+static bool
+scanint8(const char * str, int64 * result)
 {
-	const char *ptr = str;
-	int64		tmp = 0;
-	bool		neg = false;
+	pg_strtoint_status stat = pg_strtoint64(str, result);
 
-	/*
-	 * Do our own scan, rather than relying on sscanf which might be broken
-	 * for long long.
-	 *
-	 * As INT64_MIN can't be stored as a positive 64 bit integer, accumulate
-	 * value as a negative number.
-	 */
-
-	/* skip leading spaces */
-	while (*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_s64_overflow(tmp, 10, &tmp)) ||
-			unlikely(pg_sub_s64_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)
+	if (likely(stat == PG_STRTOINT_OK))
+		return true;
+	else if (stat == PG_STRTOINT_RANGE_ERROR)
 	{
-		/* could fail if input is most negative number */
-		if (unlikely(tmp == PG_INT64_MIN))
-			goto out_of_range;
-		tmp = -tmp;
-	}
-
-	*result = tmp;
-	return true;
-
-out_of_range:
-	if (!errorOK)
 		ereport(ERROR,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("value \"%s\" is out of range for type %s",
 						str, "bigint")));
-	return false;
-
-invalid_syntax:
-	if (!errorOK)
+		return false;
+	}
+	else if (stat == PG_STRTOINT_SYNTAX_ERROR)
+	{
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 				 errmsg("invalid input syntax for type %s: \"%s\"",
 						"bigint", str)));
-	return false;
+		return false;
+	}
+	else
+		/* cannot get here */
+		Assert(0);
 }
 
 /* int8in()
@@ -138,7 +83,7 @@ int8in(PG_FUNCTION_ARGS)
 	char	   *str = PG_GETARG_CSTRING(0);
 	int64		result;
 
-	(void) scanint8(str, false, &result);
+	(void) scanint8(str, &result);
 	PG_RETURN_INT64(result);
 }
 
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index a00db3ce7a..bc02958950 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -39,7 +39,6 @@
 #include "utils/float.h"
 #include "utils/guc.h"
 #include "utils/hashutils.h"
-#include "utils/int8.h"
 #include "utils/numeric.h"
 #include "utils/sortsupport.h"
 
diff --git a/src/backend/utils/adt/numutils.c b/src/backend/utils/adt/numutils.c
index 70138feb29..f5d488c569 100644
--- a/src/backend/utils/adt/numutils.c
+++ b/src/backend/utils/adt/numutils.c
@@ -543,25 +543,3 @@ pg_ltostr(char *str, int32 value)
 
 	return end;
 }
-
-/*
- * pg_strtouint64
- *		Converts 'str' into an unsigned 64-bit integer.
- *
- * This has the identical API to strtoul(3), except that it will handle
- * 64-bit ints even where "long" is narrower than that.
- *
- * For the moment it seems sufficient to assume that the platform has
- * such a function somewhere; let's not roll our own.
- */
-uint64
-pg_strtouint64(const char *str, char **endptr, int base)
-{
-#ifdef _MSC_VER					/* MSVC only */
-	return _strtoui64(str, endptr, base);
-#elif defined(HAVE_STRTOULL) && SIZEOF_LONG < 8
-	return strtoull(str, endptr, base);
-#else
-	return strtoul(str, endptr, base);
-#endif
-}
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index e5c7e5c7ee..749fc44623 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -37,7 +37,6 @@
 #include "utils/builtins.h"
 #include "utils/date.h"
 #include "utils/hashutils.h"
-#include "utils/int8.h"
 #include "utils/lsyscache.h"
 #include "utils/rangetypes.h"
 #include "utils/timestamp.h"
diff --git a/src/bin/pgbench/exprscan.l b/src/bin/pgbench/exprscan.l
index e9020ad231..99db6c72eb 100644
--- a/src/bin/pgbench/exprscan.l
+++ b/src/bin/pgbench/exprscan.l
@@ -23,6 +23,7 @@
  *-------------------------------------------------------------------------
  */
 
+#include "common/string.h"
 #include "fe_utils/psqlscan_int.h"
 
 /* context information for reporting errors in expressions */
@@ -205,7 +206,7 @@ notnull			[Nn][Oo][Tt][Nn][Uu][Ll][Ll]
 					return MAXINT_PLUS_ONE_CONST;
 				}
 {digit}+		{
-					if (!strtoint64(yytext, true, &yylval->ival))
+					if (unlikely(pg_strtoint64(yytext, &yylval->ival) != PG_STRTOINT_OK))
 						expr_yyerror_more(yyscanner, "bigint constant overflow",
 										  strdup(yytext));
 					return INTEGER_CONST;
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 570cf3306a..c51e101b54 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -34,6 +34,7 @@
 #include "postgres_fe.h"
 #include "common/int.h"
 #include "common/logging.h"
+#include "common/string.h"
 #include "fe_utils/conditional.h"
 #include "getopt_long.h"
 #include "libpq-fe.h"
@@ -670,8 +671,8 @@ is_an_int(const char *str)
 {
 	const char *ptr = str;
 
-	/* skip leading spaces; cast is consistent with strtoint64 */
-	while (*ptr && isspace((unsigned char) *ptr))
+	/* skip leading spaces; cast is consistent with pg_strtoint64 */
+	while (isspace((unsigned char) *ptr))
 		ptr++;
 
 	/* skip sign */
@@ -679,101 +680,21 @@ is_an_int(const char *str)
 		ptr++;
 
 	/* at least one digit */
-	if (*ptr && !isdigit((unsigned char) *ptr))
+	if (!isdigit((unsigned char) *ptr++))
 		return false;
 
-	/* eat all digits */
-	while (*ptr && isdigit((unsigned char) *ptr))
+	/* eat all other digits */
+	while (isdigit((unsigned char) *ptr))
+		ptr++;
+
+	/* allow trailing spaces */
+	while (isspace((unsigned char) *ptr))
 		ptr++;
 
 	/* must have reached end of string */
 	return *ptr == '\0';
 }
 
-
-/*
- * strtoint64 -- convert a string to 64-bit integer
- *
- * This function is a slightly modified version of scanint8() from
- * src/backend/utils/adt/int8.c.
- *
- * The function returns whether the conversion worked, and if so
- * "*result" is set to the result.
- *
- * If not errorOK, an error message is also printed out on errors.
- */
-bool
-strtoint64(const char *str, bool errorOK, int64 *result)
-{
-	const char *ptr = str;
-	int64		tmp = 0;
-	bool		neg = false;
-
-	/*
-	 * Do our own scan, rather than relying on sscanf which might be broken
-	 * for long long.
-	 *
-	 * As INT64_MIN can't be stored as a positive 64 bit integer, accumulate
-	 * value as a negative number.
-	 */
-
-	/* skip leading spaces */
-	while (*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_s64_overflow(tmp, 10, &tmp)) ||
-			unlikely(pg_sub_s64_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)
-	{
-		if (unlikely(tmp == PG_INT64_MIN))
-			goto out_of_range;
-		tmp = -tmp;
-	}
-
-	*result = tmp;
-	return true;
-
-out_of_range:
-	if (!errorOK)
-		fprintf(stderr,
-				"value \"%s\" is out of range for type bigint\n", str);
-	return false;
-
-invalid_syntax:
-	if (!errorOK)
-		fprintf(stderr,
-				"invalid input syntax for type bigint: \"%s\"\n", str);
-	return false;
-}
-
 /* convert string to double, detecting overflows/underflows */
 bool
 strtodouble(const char *str, bool errorOK, double *dv)
@@ -1283,6 +1204,22 @@ getVariable(CState *st, char *name)
 	return var->svalue;
 }
 
+static bool
+str2int64(const char *str, int64 *val)
+{
+	pg_strtoint_status		stat = pg_strtoint64(str, val);
+
+	if (likely(stat == PG_STRTOINT_OK))
+		return true;
+
+	if (stat == PG_STRTOINT_RANGE_ERROR)
+		fprintf(stderr, "value \"%s\" is out of range for type %s\n", str, "int64");
+	else if (stat == PG_STRTOINT_SYNTAX_ERROR)
+		fprintf(stderr, "invalid input syntax for type %s: \"%s\"", "int64", str);
+
+	return false;
+}
+
 /* Try to convert variable to a value; return false on failure */
 static bool
 makeVariableValue(Variable *var)
@@ -1325,7 +1262,7 @@ makeVariableValue(Variable *var)
 		/* if it looks like an int, it must be an int without overflow */
 		int64		iv;
 
-		if (!strtoint64(var->svalue, false, &iv))
+		if (!str2int64(var->svalue, &iv))
 			return false;
 
 		setIntValue(&var->value, iv);
diff --git a/src/bin/pgbench/pgbench.h b/src/bin/pgbench/pgbench.h
index c4a1e298e0..da3f8fa055 100644
--- a/src/bin/pgbench/pgbench.h
+++ b/src/bin/pgbench/pgbench.h
@@ -160,7 +160,6 @@ extern void syntax_error(const char *source, int lineno, const char *line,
 						 const char *cmd, const char *msg,
 						 const char *more, int col) pg_attribute_noreturn();
 
-extern bool strtoint64(const char *str, bool errorOK, int64 *pi);
 extern bool strtodouble(const char *str, bool errorOK, double *pd);
 
 #endif							/* PGBENCH_H */
diff --git a/src/common/string.c b/src/common/string.c
index c9b8482cb0..da0c5ee43d 100644
--- a/src/common/string.c
+++ b/src/common/string.c
@@ -22,6 +22,7 @@
 #endif
 
 #include "common/string.h"
+#include "common/int.h"
 
 
 /*
@@ -57,6 +58,132 @@ strtoint(const char *pg_restrict str, char **pg_restrict endptr, int base)
 	return (int) val;
 }
 
+/*
+ * pg_strtoint64 -- convert a string to 64-bit integer
+ *
+ * The function returns if the conversion failed, or
+ * "*result" is set to the result.
+ */
+pg_strtoint_status
+pg_strtoint64(const char *str, int64 *result)
+{
+	const char *ptr = str;
+	int64		tmp = 0;
+	bool		neg = false;
+
+	/*
+	 * Do our own scan, rather than relying on sscanf which might be broken
+	 * for long long.
+	 *
+	 * As INT64_MIN can't be stored as a positive 64 bit integer, accumulate
+	 * value as a negative number.
+	 */
+
+	/* skip leading spaces */
+	while (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)))
+		return PG_STRTOINT_SYNTAX_ERROR;
+
+	/* process digits, we know that we have one ahead */
+	do
+	{
+		int64		digit = (*ptr++ - '0');
+
+		if (unlikely(pg_mul_s64_overflow(tmp, 10, &tmp)) ||
+			unlikely(pg_sub_s64_overflow(tmp, digit, &tmp)))
+			return PG_STRTOINT_RANGE_ERROR;
+	}
+	while (isdigit((unsigned char) *ptr));
+
+	/* allow trailing whitespace */
+	while (isspace((unsigned char) *ptr))
+		ptr++;
+
+	/* but not other trailing chars */
+	if (unlikely(*ptr != '\0'))
+		return PG_STRTOINT_SYNTAX_ERROR;
+
+	if (!neg)
+	{
+		if (unlikely(tmp == PG_INT64_MIN))
+			return PG_STRTOINT_RANGE_ERROR;
+		tmp = -tmp;
+	}
+
+	*result = tmp;
+	return PG_STRTOINT_OK;
+}
+
+/*
+ * pg_strtouint64 -- convert a string to unsigned 64-bit integer
+ *
+ * The function returns if the conversion failed, or
+ * "*result" is set to the result.
+ */
+pg_strtoint_status
+pg_strtouint64(const char *str, uint64 *result)
+{
+	const char *ptr = str;
+	uint64		tmp = 0;
+
+	/* skip leading spaces */
+	while (isspace((unsigned char) *ptr))
+		ptr++;
+
+	/* handle sign */
+	if (*ptr == '+')
+		ptr++;
+	else if (unlikely(*ptr == '-'))
+		return PG_STRTOINT_SYNTAX_ERROR;
+
+	/* require at least one digit */
+	if (unlikely(!isdigit((unsigned char) *ptr)))
+		return PG_STRTOINT_SYNTAX_ERROR;
+
+	/* process digits, we know that we have one ahead */
+	do
+	{
+		uint64		next;
+
+		/* should we add pg_{mul,add}_u64_overflow variants? */
+
+		/* catch mul overflow: MAX_UINT64 / 10 == 1844674407370955161 */
+		if (unlikely(tmp > 1844674407370955161))
+			return PG_STRTOINT_RANGE_ERROR;
+
+		next = 10 * tmp + (*ptr++ - '0');;
+
+		/* catch add overflow */
+		if (unlikely(next < tmp))
+			return PG_STRTOINT_RANGE_ERROR;
+
+		tmp = next;
+	}
+	while (isdigit((unsigned char) *ptr));
+
+	/* allow trailing whitespace */
+	while (isspace((unsigned char) *ptr))
+		ptr++;
+
+	/* but not other trailing chars */
+	if (unlikely(*ptr != '\0'))
+		return PG_STRTOINT_SYNTAX_ERROR;
+
+	*result = tmp;
+	return PG_STRTOINT_OK;
+}
 
 /*
  * pg_clean_ascii -- Replace any non-ASCII chars with a '?' char
diff --git a/src/include/common/string.h b/src/include/common/string.h
index 94f653fdd7..9a8277a670 100644
--- a/src/include/common/string.h
+++ b/src/include/common/string.h
@@ -11,8 +11,19 @@
 #define COMMON_STRING_H
 
 extern bool pg_str_endswith(const char *str, const char *end);
+
 extern int	strtoint(const char *pg_restrict str, char **pg_restrict endptr,
 					 int base);
+
+typedef enum {
+	PG_STRTOINT_OK,
+	PG_STRTOINT_SYNTAX_ERROR,
+	PG_STRTOINT_RANGE_ERROR
+} pg_strtoint_status;
+
+extern pg_strtoint_status pg_strtoint64(const char *str, int64 *result);
+extern pg_strtoint_status pg_strtouint64(const char *str, uint64 *result);
+
 extern void pg_clean_ascii(char *str);
 extern int	pg_strip_crlf(char *str);
 
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 937ddb7ef0..8a0c2a5c48 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -50,7 +50,6 @@ extern void pg_ltoa(int32 l, char *a);
 extern void pg_lltoa(int64 ll, char *a);
 extern char *pg_ltostr_zeropad(char *str, int32 value, int32 minwidth);
 extern char *pg_ltostr(char *str, int32 value);
-extern uint64 pg_strtouint64(const char *str, char **endptr, int base);
 
 /* oid.c */
 extern oidvector *buildoidvector(const Oid *oids, int n);
diff --git a/src/include/utils/int8.h b/src/include/utils/int8.h
deleted file mode 100644
index 4836095d93..0000000000
--- a/src/include/utils/int8.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * int8.h
- *	  Declarations for operations on 64-bit integers.
- *
- *
- * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * src/include/utils/int8.h
- *
- * NOTES
- * These data types are supported on all 64-bit architectures, and may
- *	be supported through libraries on some 32-bit machines. If your machine
- *	is not currently supported, then please try to make it so, then post
- *	patches to the postgresql.org hackers mailing list.
- *
- *-------------------------------------------------------------------------
- */
-#ifndef INT8_H
-#define INT8_H
-
-extern bool scanint8(const char *str, bool errorOK, int64 *result);
-
-#endif							/* INT8_H */

Reply via email to