3232#endif
3333
3434#include "postgres_fe.h"
35+ #include "common/int.h"
3536#include "fe_utils/conditional.h"
36-
3737#include "getopt_long.h"
3838#include "libpq-fe.h"
3939#include "portability/instr_time.h"
@@ -662,19 +662,27 @@ is_an_int(const char *str)
662662/*
663663 * strtoint64 -- convert a string to 64-bit integer
664664 *
665- * This function is a modified version of scanint8() from
665+ * This function is a slightly modified version of scanint8() from
666666 * src/backend/utils/adt/int8.c.
667+ *
668+ * The function returns whether the conversion worked, and if so
669+ * "*result" is set to the result.
670+ *
671+ * If not errorOK, an error message is also printed out on errors.
667672 */
668- int64
669- strtoint64 (const char * str )
673+ bool
674+ strtoint64 (const char * str , bool errorOK , int64 * result )
670675{
671676 const char * ptr = str ;
672- int64 result = 0 ;
673- int sign = 1 ;
677+ int64 tmp = 0 ;
678+ bool neg = false ;
674679
675680 /*
676681 * Do our own scan, rather than relying on sscanf which might be broken
677682 * for long long.
683+ *
684+ * As INT64_MIN can't be stored as a positive 64 bit integer, accumulate
685+ * value as a negative number.
678686 */
679687
680688 /* skip leading spaces */
@@ -685,46 +693,80 @@ strtoint64(const char *str)
685693 if (* ptr == '-' )
686694 {
687695 ptr ++ ;
688-
689- /*
690- * Do an explicit check for INT64_MIN. Ugly though this is, it's
691- * cleaner than trying to get the loop below to handle it portably.
692- */
693- if (strncmp (ptr , "9223372036854775808" , 19 ) == 0 )
694- {
695- result = PG_INT64_MIN ;
696- ptr += 19 ;
697- goto gotdigits ;
698- }
699- sign = -1 ;
696+ neg = true;
700697 }
701698 else if (* ptr == '+' )
702699 ptr ++ ;
703700
704701 /* require at least one digit */
705- if (!isdigit ((unsigned char ) * ptr ))
706- fprintf ( stderr , "invalid input syntax for integer: \"%s\"\n" , str ) ;
702+ if (unlikely ( !isdigit ((unsigned char ) * ptr ) ))
703+ goto invalid_syntax ;
707704
708705 /* process digits */
709706 while (* ptr && isdigit ((unsigned char ) * ptr ))
710707 {
711- int64 tmp = result * 10 + (* ptr ++ - '0' );
708+ int8 digit = (* ptr ++ - '0' );
712709
713- if (( tmp / 10 ) != result ) /* overflow? */
714- fprintf ( stderr , "value \"%s\" is out of range for type bigint\n" , str );
715- result = tmp ;
710+ if (unlikely ( pg_mul_s64_overflow ( tmp , 10 , & tmp )) ||
711+ unlikely ( pg_sub_s64_overflow ( tmp , digit , & tmp )))
712+ goto out_of_range ;
716713 }
717714
718- gotdigits :
719-
720715 /* allow trailing whitespace, but not other trailing chars */
721716 while (* ptr != '\0' && isspace ((unsigned char ) * ptr ))
722717 ptr ++ ;
723718
724- if (* ptr != '\0' )
725- fprintf (stderr , "invalid input syntax for integer: \"%s\"\n" , str );
719+ if (unlikely (* ptr != '\0' ))
720+ goto invalid_syntax ;
721+
722+ if (!neg )
723+ {
724+ if (unlikely (tmp == PG_INT64_MIN ))
725+ goto out_of_range ;
726+ tmp = - tmp ;
727+ }
728+
729+ * result = tmp ;
730+ return true;
731+
732+ out_of_range :
733+ if (!errorOK )
734+ fprintf (stderr ,
735+ "value \"%s\" is out of range for type bigint\n" , str );
736+ return false;
726737
727- return ((sign < 0 ) ? - result : result );
738+ invalid_syntax :
739+ if (!errorOK )
740+ fprintf (stderr ,
741+ "invalid input syntax for type bigint: \"%s\"\n" ,str );
742+ return false;
743+ }
744+
745+ /* convert string to double, detecting overflows/underflows */
746+ bool
747+ strtodouble (const char * str , bool errorOK , double * dv )
748+ {
749+ char * end ;
750+
751+ errno = 0 ;
752+ * dv = strtod (str , & end );
753+
754+ if (unlikely (errno != 0 ))
755+ {
756+ if (!errorOK )
757+ fprintf (stderr ,
758+ "value \"%s\" is out of range for type double\n" , str );
759+ return false;
760+ }
761+
762+ if (unlikely (end == str || * end != '\0' ))
763+ {
764+ if (!errorOK )
765+ fprintf (stderr ,
766+ "invalid input syntax for type double: \"%s\"\n" ,str );
767+ return false;
768+ }
769+ return true;
728770}
729771
730772/* random number generator: uniform distribution from min to max inclusive */
@@ -1320,14 +1362,19 @@ makeVariableValue(Variable *var)
13201362 }
13211363 else if (is_an_int (var -> svalue ))
13221364 {
1323- setIntValue (& var -> value , strtoint64 (var -> svalue ));
1365+ /* if it looks like an int, it must be an int without overflow */
1366+ int64 iv ;
1367+
1368+ if (!strtoint64 (var -> svalue , false, & iv ))
1369+ return false;
1370+
1371+ setIntValue (& var -> value , iv );
13241372 }
13251373 else /* type should be double */
13261374 {
13271375 double dv ;
1328- char xs ;
13291376
1330- if (sscanf (var -> svalue , "%lf%c" , & dv , & xs ) != 1 )
1377+ if (! strtodouble (var -> svalue , true , & dv ) )
13311378 {
13321379 fprintf (stderr ,
13331380 "malformed variable \"%s\" value: \"%s\"\n" ,
@@ -1943,7 +1990,8 @@ evalStandardFunc(TState *thread, CState *st,
19431990 else /* we have integer operands, or % */
19441991 {
19451992 int64 li ,
1946- ri ;
1993+ ri ,
1994+ res ;
19471995
19481996 if (!coerceToInt (lval , & li ) ||
19491997 !coerceToInt (rval , & ri ))
@@ -1952,15 +2000,30 @@ evalStandardFunc(TState *thread, CState *st,
19522000 switch (func )
19532001 {
19542002 case PGBENCH_ADD :
1955- setIntValue (retval , li + ri );
2003+ if (pg_add_s64_overflow (li , ri , & res ))
2004+ {
2005+ fprintf (stderr , "bigint add out of range\n" );
2006+ return false;
2007+ }
2008+ setIntValue (retval , res );
19562009 return true;
19572010
19582011 case PGBENCH_SUB :
1959- setIntValue (retval , li - ri );
2012+ if (pg_sub_s64_overflow (li , ri , & res ))
2013+ {
2014+ fprintf (stderr , "bigint sub out of range\n" );
2015+ return false;
2016+ }
2017+ setIntValue (retval , res );
19602018 return true;
19612019
19622020 case PGBENCH_MUL :
1963- setIntValue (retval , li * ri );
2021+ if (pg_mul_s64_overflow (li , ri , & res ))
2022+ {
2023+ fprintf (stderr , "bigint mul out of range\n" );
2024+ return false;
2025+ }
2026+ setIntValue (retval , res );
19642027 return true;
19652028
19662029 case PGBENCH_EQ :
@@ -1994,7 +2057,7 @@ evalStandardFunc(TState *thread, CState *st,
19942057 /* overflow check (needed for INT64_MIN) */
19952058 if (li == PG_INT64_MIN )
19962059 {
1997- fprintf (stderr , "bigint out of range\n" );
2060+ fprintf (stderr , "bigint div out of range\n" );
19982061 return false;
19992062 }
20002063 else
0 commit comments