Bonjour Michaƫl,

Attached a rebased version which implements the int64/uint64 stuff. It is
basically the previous patch without the overflow inlined functions.

-     if (!strtoint64(yytext, true, &yylval->ival))
+     if (unlikely(pg_strtoint64(yytext, &yylval->ival) != PG_STRTOINT_OK))

It seems to me that we should have unlikely() only within
pg_strtoint64(), pg_strtouint64(), etc.

From a compiler perspective, the (un)likely tip is potentially useful on
any test. We know when parsing a that it is very unlikely that the string conversion would fail, so we can tell that, so that the compiler knows which branch it should optimize first.

You can argue against that if the functions are inlined, because maybe the compiler would propagate the information, but for distinct functions compiled separately the information is useful at each level.

-   /* 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++;

What do you think about splitting this part in two?  I would suggest
to do the refactoring in a first patch, and the consider all the
optimizations for the routines you have in mind afterwards.

I would not bother.

I think that we don't actually need is_an_int() and str2int64() at all
in pgbench.c as we could just check for the return code of
pg_strtoint64() and switch to the double parsing only if we don't have
PG_STRTOINT_OK.

Yep, you are right, now that the conversion functions does not error out a message, its failure can be used as a test.

The version attached changes slightly the semantics, because on int overflows a double conversion will be attempted instead of erroring. I do not think that it is worth the effort of preserving the previous semantic of erroring.

scanint8() only has one caller in the backend with your patch, and we
don't check after its return result in int8.c, so I would suggest to
move the error handling directly in int8in() and to remove scanint8().

Ok.

I think that we should also introduce the [u]int[16|32] flavors and
expand them in the code in a single patch, in a way consistent with
what you have done for int64/uint64 as the state that we reach with
the patch is kind of weird as there are existing versions numutils.c.

Before dealing with the 16/32 versions, which involve quite a significant amount of changes, I would want a clear message that "the 64 bit approach" is the model to follow.

Moreover, I'm unsure how to rename the existing pg_strtoint32 and others
which call ereport, if the name is used for the common error returning version.

Have you done some performance testing of the patch?  The COPY
bulkload is a good example of that:
https://www.postgresql.org/message-id/20190717181428.krqpmduejbqr4m6g%40alap3.anarazel.de

I have done no such thing for now.

I would not expect any significant performance difference when loading int8 things because basically scanint8 has just been renamed pg_strtoint64 and made global, and that is more or less all. It might be a little bit slower because possible the compiler cannot inline the conversion, but on the other hand, the *likely hints and removed tests may marginaly help performance. I think that the only way to test performance significantly would be to write a specific program that loops over a conversion.

--
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 755ca6e277..f8cc5bc48a 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..d1bc30d9f4 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,99 +47,29 @@ 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.
+/* int8in()
  */
-bool
-scanint8(const char *str, bool errorOK, int64 *result)
+Datum
+int8in(PG_FUNCTION_ARGS)
 {
-	const char *ptr = str;
-	int64		tmp = 0;
-	bool		neg = false;
+	char			   *str = PG_GETARG_CSTRING(0);
+	int64				result;
+	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)
-	{
-		/* 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)
+	if (likely(stat == PG_STRTOINT_OK))
+		PG_RETURN_INT64(result);
+	else if (stat == PG_STRTOINT_RANGE_ERROR)
 		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)
+	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;
-}
-
-/* int8in()
- */
-Datum
-int8in(PG_FUNCTION_ARGS)
-{
-	char	   *str = PG_GETARG_CSTRING(0);
-	int64		result;
-
-	(void) scanint8(str, false, &result);
-	PG_RETURN_INT64(result);
+	else
+		Assert(0); /* cannot get here */
 }
 
 
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 ed7652bfbf..6581dc4de4 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"
@@ -664,116 +665,6 @@ usage(void)
 		   progname, progname);
 }
 
-/* return whether str matches "^\s*[-+]?[0-9]+$" */
-static bool
-is_an_int(const char *str)
-{
-	const char *ptr = str;
-
-	/* skip leading spaces; cast is consistent with strtoint64 */
-	while (*ptr && isspace((unsigned char) *ptr))
-		ptr++;
-
-	/* skip sign */
-	if (*ptr == '+' || *ptr == '-')
-		ptr++;
-
-	/* at least one digit */
-	if (*ptr && !isdigit((unsigned char) *ptr))
-		return false;
-
-	/* eat all digits */
-	while (*ptr && isdigit((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,11 +1174,13 @@ getVariable(CState *st, char *name)
 	return var->svalue;
 }
 
+
 /* Try to convert variable to a value; return false on failure */
 static bool
 makeVariableValue(Variable *var)
 {
 	size_t		slen;
+	int64		ival;
 
 	if (var->value.type != PGBT_NO_VALUE)
 		return true;			/* no work */
@@ -1320,15 +1213,16 @@ makeVariableValue(Variable *var)
 	{
 		setBoolValue(&var->value, false);
 	}
-	else if (is_an_int(var->svalue))
+
+	/*
+	 * Attempt an int64 conversion.
+	 *
+	 * A too-large but syntactically correct int64 will be parsed as a double
+	 * below.
+	 */
+	else if (pg_strtoint64(var->svalue, &ival) == PG_STRTOINT_OK)
 	{
-		/* if it looks like an int, it must be an int without overflow */
-		int64		iv;
-
-		if (!strtoint64(var->svalue, false, &iv))
-			return false;
-
-		setIntValue(&var->value, iv);
+		setIntValue(&var->value, ival);
 	}
 	else						/* type should be double */
 	{
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..5487dfbb8b 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,122 @@ 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		digit = (*ptr++ - '0');
+
+		if (unlikely(pg_mul_u64_overflow(tmp, 10, &tmp)) ||
+			unlikely(pg_add_u64_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;
+
+	*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