Use strtoi64() in pgbench, replacing its open-coded implementation
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Fri, 21 Nov 2025 13:03:11 +0000 (15:03 +0200)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Fri, 21 Nov 2025 13:03:11 +0000 (15:03 +0200)
Makes the code a little simpler.

The old implementation accepted trailing whitespace, but that was
unnecessary. Firstly, its sibling function for parsing decimals,
strtodouble(), does not accept trailing whitespace. Secondly, none of
the callers can pass a string with trailing whitespace to it.

In the passing, check specifically for ERANGE before printing the "out
of range" error. On some systems, strtoul() and strtod() return EINVAL
on an empty or all-spaces string, and "invalid input syntax" is more
appropriate for that than "out of range". For the existing
strtodouble() function this is purely academical because it's never
called with errorOK==false, but let's be tidy. (Perhaps we should
remove the dead codepaths altogether, but I'll leave that for another
day.)

Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Yuefei Shi <shiyuefei1004@gmail.com>
Reviewed-by: Neil Chen <carpenter.nail.cz@gmail.com>
Discussion: https://www.postgresql.org/message-id/861dd5bd-f2c9-4ff5-8aa0-f82bdb75ec1f@iki.fi

src/bin/pgbench/pgbench.c

index a425176ecdce14861c3a371d3a77596e518a8b93..68774a59efdb22e39dbb59fd6e6c31421839e0e9 100644 (file)
@@ -982,13 +982,17 @@ usage(void)
           progname, progname, PACKAGE_BUGREPORT, PACKAGE_NAME, PACKAGE_URL);
 }
 
-/* return whether str matches "^\s*[-+]?[0-9]+$" */
+/*
+ * Return whether str matches "^\s*[-+]?[0-9]+$"
+ *
+ * This should agree with strtoint64() on what's accepted, ignoring overflows.
+ */
 static bool
 is_an_int(const char *str)
 {
    const char *ptr = str;
 
-   /* skip leading spaces; cast is consistent with strtoint64 */
+   /* skip leading spaces */
    while (*ptr && isspace((unsigned char) *ptr))
        ptr++;
 
@@ -1012,9 +1016,6 @@ is_an_int(const char *str)
 /*
  * strtoint64 -- convert a string to 64-bit integer
  *
- * This function is a slightly modified version of pg_strtoint64() from
- * src/backend/utils/adt/numutils.c.
- *
  * The function returns whether the conversion worked, and if so
  * "*result" is set to the result.
  *
@@ -1023,71 +1024,25 @@ is_an_int(const char *str)
 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++;
+   char       *end;
 
-   /* require at least one digit */
-   if (unlikely(!isdigit((unsigned char) *ptr)))
-       goto invalid_syntax;
+   errno = 0;
+   *result = strtoi64(str, &end, 10);
 
-   /* process digits */
-   while (*ptr && isdigit((unsigned char) *ptr))
+   if (unlikely(errno == ERANGE))
    {
-       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;
+       if (!errorOK)
+           pg_log_error("value \"%s\" is out of range for type bigint", str);
+       return false;
    }
 
-   /* 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(errno != 0 || end == str || *end != '\0'))
    {
-       if (unlikely(tmp == PG_INT64_MIN))
-           goto out_of_range;
-       tmp = -tmp;
+       if (!errorOK)
+           pg_log_error("invalid input syntax for type bigint: \"%s\"", str);
+       return false;
    }
-
-   *result = tmp;
    return true;
-
-out_of_range:
-   if (!errorOK)
-       pg_log_error("value \"%s\" is out of range for type bigint", str);
-   return false;
-
-invalid_syntax:
-   if (!errorOK)
-       pg_log_error("invalid input syntax for type bigint: \"%s\"", str);
-   return false;
 }
 
 /* convert string to double, detecting overflows/underflows */
@@ -1099,14 +1054,14 @@ strtodouble(const char *str, bool errorOK, double *dv)
    errno = 0;
    *dv = strtod(str, &end);
 
-   if (unlikely(errno != 0))
+   if (unlikely(errno == ERANGE))
    {
        if (!errorOK)
            pg_log_error("value \"%s\" is out of range for type double", str);
        return false;
    }
 
-   if (unlikely(end == str || *end != '\0'))
+   if (unlikely(errno != 0 || end == str || *end != '\0'))
    {
        if (!errorOK)
            pg_log_error("invalid input syntax for type double: \"%s\"", str);