@@ -151,6 +151,15 @@ char *index_tablespace = NULL;
151151#define ntellers 10
152152#define naccounts 100000
153153
154+ /*
155+ * The scale factor at/beyond which 32bit integers are incapable of storing
156+ * 64bit values.
157+ *
158+ * Although the actual threshold is 21474, we use 20000 because it is easier to
159+ * document and remember, and isn't that far away from the real threshold.
160+ */
161+ #define SCALE_32BIT_THRESHOLD 20000
162+
154163bool use_log ; /* log transaction latencies to a file */
155164bool use_quiet ; /* quiet logging onto stderr */
156165bool is_connect ; /* establish connection for each transaction */
@@ -403,9 +412,77 @@ usage(void)
403412 progname , progname );
404413}
405414
415+ /*
416+ * strtoint64 -- convert a string to 64-bit integer
417+ *
418+ * This function is a modified version of scanint8() from
419+ * src/backend/utils/adt/int8.c.
420+ */
421+ static int64
422+ strtoint64 (const char * str )
423+ {
424+ const char * ptr = str ;
425+ int64 result = 0 ;
426+ int sign = 1 ;
427+
428+ /*
429+ * Do our own scan, rather than relying on sscanf which might be broken
430+ * for long long.
431+ */
432+
433+ /* skip leading spaces */
434+ while (* ptr && isspace ((unsigned char ) * ptr ))
435+ ptr ++ ;
436+
437+ /* handle sign */
438+ if (* ptr == '-' )
439+ {
440+ ptr ++ ;
441+
442+ /*
443+ * Do an explicit check for INT64_MIN. Ugly though this is, it's
444+ * cleaner than trying to get the loop below to handle it portably.
445+ */
446+ if (strncmp (ptr , "9223372036854775808" , 19 ) == 0 )
447+ {
448+ result = - INT64CONST (0x7fffffffffffffff ) - 1 ;
449+ ptr += 19 ;
450+ goto gotdigits ;
451+ }
452+ sign = -1 ;
453+ }
454+ else if (* ptr == '+' )
455+ ptr ++ ;
456+
457+ /* require at least one digit */
458+ if (!isdigit ((unsigned char ) * ptr ))
459+ fprintf (stderr , "invalid input syntax for integer: \"%s\"\n" , str );
460+
461+ /* process digits */
462+ while (* ptr && isdigit ((unsigned char ) * ptr ))
463+ {
464+ int64 tmp = result * 10 + (* ptr ++ - '0' );
465+
466+ if ((tmp / 10 ) != result ) /* overflow? */
467+ fprintf (stderr , "value \"%s\" is out of range for type bigint\n" , str );
468+ result = tmp ;
469+ }
470+
471+ gotdigits :
472+
473+ /* allow trailing whitespace, but not other trailing chars */
474+ while (* ptr != '\0' && isspace ((unsigned char ) * ptr ))
475+ ptr ++ ;
476+
477+ if (* ptr != '\0' )
478+ fprintf (stderr , "invalid input syntax for integer: \"%s\"\n" , str );
479+
480+ return ((sign < 0 ) ? - result : result );
481+ }
482+
406483/* random number generator: uniform distribution from min to max inclusive */
407- static int
408- getrand (TState * thread , int min , int max )
484+ static int64
485+ getrand (TState * thread , int64 min , int64 max )
409486{
410487 /*
411488 * Odd coding is so that min and max have approximately the same chance of
@@ -416,7 +493,7 @@ getrand(TState *thread, int min, int max)
416493 * protected by a mutex, and therefore a bottleneck on machines with many
417494 * CPUs.
418495 */
419- return min + (int ) ((max - min + 1 ) * pg_erand48 (thread -> random_state ));
496+ return min + (int64 ) ((max - min + 1 ) * pg_erand48 (thread -> random_state ));
420497}
421498
422499/* call PQexec() and exit() on failure */
@@ -960,7 +1037,7 @@ doCustom(TState *thread, CState *st, instr_time *conn_time, FILE *logfile)
9601037 if (commands [st -> state ] == NULL )
9611038 {
9621039 st -> state = 0 ;
963- st -> use_file = getrand (thread , 0 , num_files - 1 );
1040+ st -> use_file = ( int ) getrand (thread , 0 , num_files - 1 );
9641041 commands = sql_files [st -> use_file ];
9651042 }
9661043 }
@@ -1080,7 +1157,7 @@ doCustom(TState *thread, CState *st, instr_time *conn_time, FILE *logfile)
10801157 if (pg_strcasecmp (argv [0 ], "setrandom" ) == 0 )
10811158 {
10821159 char * var ;
1083- int min ,
1160+ int64 min ,
10841161 max ;
10851162 char res [64 ];
10861163
@@ -1092,10 +1169,10 @@ doCustom(TState *thread, CState *st, instr_time *conn_time, FILE *logfile)
10921169 st -> ecnt ++ ;
10931170 return true;
10941171 }
1095- min = atoi (var );
1172+ min = strtoint64 (var );
10961173 }
10971174 else
1098- min = atoi (argv [2 ]);
1175+ min = strtoint64 (argv [2 ]);
10991176
11001177#ifdef NOT_USED
11011178 if (min < 0 )
@@ -1114,10 +1191,10 @@ doCustom(TState *thread, CState *st, instr_time *conn_time, FILE *logfile)
11141191 st -> ecnt ++ ;
11151192 return true;
11161193 }
1117- max = atoi (var );
1194+ max = strtoint64 (var );
11181195 }
11191196 else
1120- max = atoi (argv [3 ]);
1197+ max = strtoint64 (argv [3 ]);
11211198
11221199 if (max < min )
11231200 {
@@ -1127,8 +1204,8 @@ doCustom(TState *thread, CState *st, instr_time *conn_time, FILE *logfile)
11271204 }
11281205
11291206 /*
1130- * getrand() neeeds to be able to subtract max from min and add
1131- * one the result without overflowing. Since we know max > min,
1207+ * getrand() needs to be able to subtract max from min and add
1208+ * one to the result without overflowing. Since we know max > min,
11321209 * we can detect overflow just by checking for a negative result.
11331210 * But we must check both that the subtraction doesn't overflow,
11341211 * and that adding one to the result doesn't overflow either.
@@ -1141,9 +1218,9 @@ doCustom(TState *thread, CState *st, instr_time *conn_time, FILE *logfile)
11411218 }
11421219
11431220#ifdef DEBUG
1144- printf ("min: %d max: %d random: %d \n" , min , max , getrand (thread , min , max ));
1221+ printf ("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT " \n" , min , max , getrand (thread , min , max ));
11451222#endif
1146- snprintf (res , sizeof (res ), "%d" , getrand (thread , min , max ));
1223+ snprintf (res , sizeof (res ), INT64_FORMAT , getrand (thread , min , max ));
11471224
11481225 if (!putVariable (st , argv [0 ], argv [1 ], res ))
11491226 {
@@ -1156,7 +1233,7 @@ doCustom(TState *thread, CState *st, instr_time *conn_time, FILE *logfile)
11561233 else if (pg_strcasecmp (argv [0 ], "set" ) == 0 )
11571234 {
11581235 char * var ;
1159- int ope1 ,
1236+ int64 ope1 ,
11601237 ope2 ;
11611238 char res [64 ];
11621239
@@ -1168,13 +1245,13 @@ doCustom(TState *thread, CState *st, instr_time *conn_time, FILE *logfile)
11681245 st -> ecnt ++ ;
11691246 return true;
11701247 }
1171- ope1 = atoi (var );
1248+ ope1 = strtoint64 (var );
11721249 }
11731250 else
1174- ope1 = atoi (argv [2 ]);
1251+ ope1 = strtoint64 (argv [2 ]);
11751252
11761253 if (argc < 5 )
1177- snprintf (res , sizeof (res ), "%d" , ope1 );
1254+ snprintf (res , sizeof (res ), INT64_FORMAT , ope1 );
11781255 else
11791256 {
11801257 if (* argv [4 ] == ':' )
@@ -1185,17 +1262,17 @@ doCustom(TState *thread, CState *st, instr_time *conn_time, FILE *logfile)
11851262 st -> ecnt ++ ;
11861263 return true;
11871264 }
1188- ope2 = atoi (var );
1265+ ope2 = strtoint64 (var );
11891266 }
11901267 else
1191- ope2 = atoi (argv [4 ]);
1268+ ope2 = strtoint64 (argv [4 ]);
11921269
11931270 if (strcmp (argv [3 ], "+" ) == 0 )
1194- snprintf (res , sizeof (res ), "%d" , ope1 + ope2 );
1271+ snprintf (res , sizeof (res ), INT64_FORMAT , ope1 + ope2 );
11951272 else if (strcmp (argv [3 ], "-" ) == 0 )
1196- snprintf (res , sizeof (res ), "%d" , ope1 - ope2 );
1273+ snprintf (res , sizeof (res ), INT64_FORMAT , ope1 - ope2 );
11971274 else if (strcmp (argv [3 ], "*" ) == 0 )
1198- snprintf (res , sizeof (res ), "%d" , ope1 * ope2 );
1275+ snprintf (res , sizeof (res ), INT64_FORMAT , ope1 * ope2 );
11991276 else if (strcmp (argv [3 ], "/" ) == 0 )
12001277 {
12011278 if (ope2 == 0 )
@@ -1204,7 +1281,7 @@ doCustom(TState *thread, CState *st, instr_time *conn_time, FILE *logfile)
12041281 st -> ecnt ++ ;
12051282 return true;
12061283 }
1207- snprintf (res , sizeof (res ), "%d" , ope1 / ope2 );
1284+ snprintf (res , sizeof (res ), INT64_FORMAT , ope1 / ope2 );
12081285 }
12091286 else
12101287 {
@@ -1311,6 +1388,15 @@ disconnect_all(CState *state, int length)
13111388static void
13121389init (bool is_no_vacuum )
13131390{
1391+
1392+ /* The scale factor at/beyond which 32bit integers are incapable of storing
1393+ * 64bit values.
1394+ *
1395+ * Although the actual threshold is 21474, we use 20000 because it is easier to
1396+ * document and remember, and isn't that far away from the real threshold.
1397+ */
1398+ #define SCALE_32BIT_THRESHOLD 20000
1399+
13141400 /*
13151401 * Note: TPC-B requires at least 100 bytes per row, and the "filler"
13161402 * fields in these table declarations were intended to comply with that.
@@ -1329,7 +1415,9 @@ init(bool is_no_vacuum)
13291415 struct ddlinfo DDLs [] = {
13301416 {
13311417 "pgbench_history" ,
1332- "tid int,bid int,aid int,delta int,mtime timestamp,filler char(22)" ,
1418+ scale >= SCALE_32BIT_THRESHOLD
1419+ ? "tid int,bid int,aid bigint,delta int,mtime timestamp,filler char(22)"
1420+ : "tid int,bid int,aid int,delta int,mtime timestamp,filler char(22)" ,
13331421 0
13341422 },
13351423 {
@@ -1339,7 +1427,9 @@ init(bool is_no_vacuum)
13391427 },
13401428 {
13411429 "pgbench_accounts" ,
1342- "aid int not null,bid int,abalance int,filler char(84)" ,
1430+ scale >= SCALE_32BIT_THRESHOLD
1431+ ? "aid bigint not null,bid int,abalance int,filler char(84)"
1432+ : "aid int not null,bid int,abalance int,filler char(84)" ,
13431433 1
13441434 },
13451435 {
@@ -1365,6 +1455,7 @@ init(bool is_no_vacuum)
13651455 PGresult * res ;
13661456 char sql [256 ];
13671457 int i ;
1458+ int64 k ;
13681459
13691460 /* used to track elapsed time and estimate of the remaining time */
13701461 instr_time start , diff ;
@@ -1441,11 +1532,11 @@ init(bool is_no_vacuum)
14411532
14421533 INSTR_TIME_SET_CURRENT (start );
14431534
1444- for (i = 0 ; i < naccounts * scale ; i ++ )
1535+ for (k = 0 ; k < ( int64 ) naccounts * scale ; k ++ )
14451536 {
1446- int j = i + 1 ;
1537+ int64 j = k + 1 ;
14471538
1448- snprintf (sql , 256 , "%d\t%d \t%d\t\n" , j , i / naccounts + 1 , 0 );
1539+ snprintf (sql , 256 , INT64_FORMAT "\t" INT64_FORMAT " \t%d\t\n" , j , k / naccounts + 1 , 0 );
14491540 if (PQputline (con , sql ))
14501541 {
14511542 fprintf (stderr , "PQputline failed\n" );
@@ -1462,8 +1553,8 @@ init(bool is_no_vacuum)
14621553 elapsed_sec = INSTR_TIME_GET_DOUBLE (diff );
14631554 remaining_sec = (scale * naccounts - j ) * elapsed_sec / j ;
14641555
1465- fprintf (stderr , "%d of %d tuples (%d%%) done (elapsed %.2f s, remaining %.2f s).\n" ,
1466- j , naccounts * scale ,
1556+ fprintf (stderr , INT64_FORMAT " of " INT64_FORMAT " tuples (%d%%) done (elapsed %.2f s, remaining %.2f s).\n" ,
1557+ j , ( int64 ) naccounts * scale ,
14671558 (int ) (((int64 ) j * 100 ) / (naccounts * scale )),
14681559 elapsed_sec , remaining_sec );
14691560 }
@@ -1479,8 +1570,8 @@ init(bool is_no_vacuum)
14791570 /* have we reached the next interval (or end)? */
14801571 if ((j == scale * naccounts ) || (elapsed_sec >= log_interval * LOG_STEP_SECONDS )) {
14811572
1482- fprintf (stderr , "%d of %d tuples (%d%%) done (elapsed %.2f s, remaining %.2f s).\n" ,
1483- j , naccounts * scale ,
1573+ fprintf (stderr , INT64_FORMAT " of " INT64_FORMAT " tuples (%d%%) done (elapsed %.2f s, remaining %.2f s).\n" ,
1574+ j , ( int64 ) naccounts * scale ,
14841575 (int ) (((int64 ) j * 100 ) / (naccounts * scale )), elapsed_sec , remaining_sec );
14851576
14861577 /* skip to the next interval */
0 commit comments