Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 89d00cb

Browse files
committed
Allow pgbench to use a scale larger than 21474.
Beyond 21474, the number of accounts exceed the range for int4. Change the initialization code to use bigint for account id columns when scale is large enough, and switch to using int64s for the variables in pgbench code. The threshold where we switch to bigints is set at 20000, because that's easier to remember and document than 21474, and ensures that there is some headroom when int4s are used. Greg Smith, with various changes by Euler Taveira de Oliveira, Gurjeet Singh and Satoshi Nagayasu.
1 parent c9d7dba commit 89d00cb

File tree

2 files changed

+128
-32
lines changed

2 files changed

+128
-32
lines changed

contrib/pgbench/pgbench.c

+123-32
Original file line numberDiff line numberDiff line change
@@ -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+
154163
bool use_log; /* log transaction latencies to a file */
155164
bool use_quiet; /* quiet logging onto stderr */
156165
bool 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)
13111388
static void
13121389
init(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 */

doc/src/sgml/pgbench.sgml

+5
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,11 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
185185
Multiply the number of rows generated by the scale factor.
186186
For example, <literal>-s 100</> will create 10,000,000 rows
187187
in the <structname>pgbench_accounts</> table. Default is 1.
188+
When the scale is 20,000 or larger, the columns used to
189+
hold account identifiers (<structfield>aid</structfield> columns)
190+
will switch to using larger integers (<type>bigint</type>),
191+
in order to be big enough to hold the range of account
192+
identifiers.
188193
</para>
189194
</listitem>
190195
</varlistentry>

0 commit comments

Comments
 (0)