Bonjour Michaël,
- *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.
Still this makes the checks less robust?
I do not see any downside, but maybe I lack imagination.
On my laptop isWHATEVER is implementd through an array mapping characters
to a bitfield saying whether each char is WHATEVER, depending on the bit.
This array is well defined for index 0 ('\0').
If an implementation is based on comparisons, for isdigit it would be:
c >= '0' && c <= '9'
Then checking c != '\0' is redundant with c >= '0'.
Given the way the character checks are used in the function, we do not go
beyond the end of the string because we only proceed further when a
character is actually recognized, else we return.
So I cannot see any robustness issue, just a few cycles saved.
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…
Mr Freund has mentioned that here:
https://www.postgresql.org/message-id/20190717184820.iqz7schxdbucm...@alap3.anarazel.de
Yep, that is what I mean by expensive: there is an integer division, which
is avoided if b is known to be 10, hence is not zero and the limit value
can be checked directly on the input without having to perform a division
each time.
So I was pretty happy with my two discreet, small and efficient tests.
That's also a matter of code and interface consistency IMHO.
Possibly.
ISTM that part of the motivation is to reduce costs for heavily used
conversions, eg on COPY. Function "scanf" is overly expensive because it
has to interpret its format, and we have to check for overflows…
Anyway, if we assume that the builtins exist and rely on efficient
hardware check, maybe we do not care about the fallback cases which would
just be slow but never executed.
Note that all this is moot, as all instances of string to uint64
conversion do not check for errors.
Attached v7 does create uint64 overflow inline functions. The stuff yet is
not moved to "common/int.c", a file which does not exists, even if
"common/int.h" does.
--
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..a6fe596893 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,24 @@ 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);
+ else
+ Assert(0); /* cannot happen */
+
+ return false;
+}
+
/* Try to convert variable to a value; return false on failure */
static bool
makeVariableValue(Variable *var)
@@ -1325,7 +1264,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..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/int.h b/src/include/common/int.h
index d754798543..c52b446e98 100644
--- a/src/include/common/int.h
+++ b/src/include/common/int.h
@@ -270,4 +270,94 @@ pg_mul_s64_overflow(int64 a, int64 b, int64 *result)
#endif
}
+/*
+ * If a + b overflows, return true, otherwise store the result of a + b into
+ * *result. The content of *result is implementation defined in case of
+ * overflow.
+ */
+static inline bool
+pg_add_u64_overflow(uint64 a, uint64 b, uint64 *result)
+{
+#if defined(HAVE__BUILTIN_OP_OVERFLOW)
+ return __builtin_add_overflow(a, b, result);
+#elif defined(HAVE_INT128)
+ int128 res = (int128) a + (int128) b;
+
+ if (unlikely(res > PG_UINT64_MAX))
+ {
+ *result = 0x5EED; /* to avoid spurious warnings */
+ return true;
+ }
+ *result = (uint64) res;
+ return false;
+#else
+ uint64 res = a + b;
+
+ if (unlikely(res < a))
+ {
+ *result = 0x5EED; /* to avoid spurious warnings */
+ return true;
+ }
+ *result = res;
+ return false;
+#endif
+}
+
+/*
+ * If a - b overflows, return true, otherwise store the result of a - b into
+ * *result. The content of *result is implementation defined in case of
+ * overflow.
+ */
+static inline bool
+pg_sub_u64_overflow(uint64 a, uint64 b, uint64 *result)
+{
+#if defined(HAVE__BUILTIN_OP_OVERFLOW)
+ return __builtin_sub_overflow(a, b, result);
+#else
+ /* check for underflow, really */
+ if (b > a)
+ {
+ *result = 0x5EED; /* to avoid spurious warnings */
+ return true;
+ }
+ *result = a - b;
+ return false;
+#endif
+}
+
+/*
+ * If a * b overflows, return true, otherwise store the result of a * b into
+ * *result. The content of *result is implementation defined in case of
+ * overflow.
+ */
+static inline bool
+pg_mul_u64_overflow(uint64 a, uint64 b, uint64 *result)
+{
+#if defined(HAVE__BUILTIN_OP_OVERFLOW)
+ return __builtin_mul_overflow(a, b, result);
+#elif defined(HAVE_INT128)
+ int128 res = (int128) a * (int128) b;
+
+ /* res < 0 is triggered if we overflow int128 mul */
+ if (unlikely(res > PG_UINT64_MAX || res < 0))
+ {
+ *result = 0x5EED; /* to avoid spurious warnings */
+ return true;
+ }
+ *result = (uint64) res;
+ return false;
+#else
+ /* hopefully this quite expensive version is never used */
+ uint64 res = a * b;
+
+ if (unlikely(b != 0 && a != res / b))
+ {
+ *result = 0x5EED; /* to avoid spurious warnings */
+ return true;
+ }
+ *result = res;
+ return false;
+#endif
+}
+
#endif /* COMMON_INT_H */
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 */