32
32
#endif
33
33
34
34
#include "postgres_fe.h"
35
+ #include "common/int.h"
35
36
#include "fe_utils/conditional.h"
36
-
37
37
#include "getopt_long.h"
38
38
#include "libpq-fe.h"
39
39
#include "portability/instr_time.h"
@@ -662,19 +662,27 @@ is_an_int(const char *str)
662
662
/*
663
663
* strtoint64 -- convert a string to 64-bit integer
664
664
*
665
- * This function is a modified version of scanint8() from
665
+ * This function is a slightly modified version of scanint8() from
666
666
* 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.
667
672
*/
668
- int64
669
- strtoint64 (const char * str )
673
+ bool
674
+ strtoint64 (const char * str , bool errorOK , int64 * result )
670
675
{
671
676
const char * ptr = str ;
672
- int64 result = 0 ;
673
- int sign = 1 ;
677
+ int64 tmp = 0 ;
678
+ bool neg = false ;
674
679
675
680
/*
676
681
* Do our own scan, rather than relying on sscanf which might be broken
677
682
* 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.
678
686
*/
679
687
680
688
/* skip leading spaces */
@@ -685,46 +693,80 @@ strtoint64(const char *str)
685
693
if (* ptr == '-' )
686
694
{
687
695
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;
700
697
}
701
698
else if (* ptr == '+' )
702
699
ptr ++ ;
703
700
704
701
/* 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 ;
707
704
708
705
/* process digits */
709
706
while (* ptr && isdigit ((unsigned char ) * ptr ))
710
707
{
711
- int64 tmp = result * 10 + (* ptr ++ - '0' );
708
+ int8 digit = (* ptr ++ - '0' );
712
709
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 ;
716
713
}
717
714
718
- gotdigits :
719
-
720
715
/* allow trailing whitespace, but not other trailing chars */
721
716
while (* ptr != '\0' && isspace ((unsigned char ) * ptr ))
722
717
ptr ++ ;
723
718
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;
726
737
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;
728
770
}
729
771
730
772
/* random number generator: uniform distribution from min to max inclusive */
@@ -1320,14 +1362,19 @@ makeVariableValue(Variable *var)
1320
1362
}
1321
1363
else if (is_an_int (var -> svalue ))
1322
1364
{
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 );
1324
1372
}
1325
1373
else /* type should be double */
1326
1374
{
1327
1375
double dv ;
1328
- char xs ;
1329
1376
1330
- if (sscanf (var -> svalue , "%lf%c" , & dv , & xs ) != 1 )
1377
+ if (! strtodouble (var -> svalue , true , & dv ) )
1331
1378
{
1332
1379
fprintf (stderr ,
1333
1380
"malformed variable \"%s\" value: \"%s\"\n" ,
@@ -1943,7 +1990,8 @@ evalStandardFunc(TState *thread, CState *st,
1943
1990
else /* we have integer operands, or % */
1944
1991
{
1945
1992
int64 li ,
1946
- ri ;
1993
+ ri ,
1994
+ res ;
1947
1995
1948
1996
if (!coerceToInt (lval , & li ) ||
1949
1997
!coerceToInt (rval , & ri ))
@@ -1952,15 +2000,30 @@ evalStandardFunc(TState *thread, CState *st,
1952
2000
switch (func )
1953
2001
{
1954
2002
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 );
1956
2009
return true;
1957
2010
1958
2011
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 );
1960
2018
return true;
1961
2019
1962
2020
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 );
1964
2027
return true;
1965
2028
1966
2029
case PGBENCH_EQ :
@@ -1994,7 +2057,7 @@ evalStandardFunc(TState *thread, CState *st,
1994
2057
/* overflow check (needed for INT64_MIN) */
1995
2058
if (li == PG_INT64_MIN )
1996
2059
{
1997
- fprintf (stderr , "bigint out of range\n" );
2060
+ fprintf (stderr , "bigint div out of range\n" );
1998
2061
return false;
1999
2062
}
2000
2063
else
0 commit comments