4 * A simple benchmark program for PostgreSQL
5 * Originally written by Tatsuo Ishii and enhanced by many contributors.
8 * Copyright (c) 2000-2009, PostgreSQL Global Development Group
11 * Permission to use, copy, modify, and distribute this software and its
12 * documentation for any purpose, without fee, and without a written agreement
13 * is hereby granted, provided that the above copyright notice and this
14 * paragraph and the following two paragraphs appear in all copies.
16 * IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
17 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
18 * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
19 * DOCUMENTATION, EVEN IF THE AUTHOR OR DISTRIBUTORS HAVE BEEN ADVISED OF THE
20 * POSSIBILITY OF SUCH DAMAGE.
22 * THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIMS ANY WARRANTIES,
23 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
24 * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
25 * ON AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAS NO OBLIGATIONS TO
26 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
29 #include "postgres_fe.h"
38 #define FD_SETSIZE 1024
50 #ifdef HAVE_SYS_SELECT_H
51 #include <sys/select.h>
54 #ifdef HAVE_SYS_RESOURCE_H
55 #include <sys/resource.h> /* for getrlimit */
62 /********************************************************************
63 * some configurable parameters */
65 /* max number of clients allowed */
67 #define MAXCLIENTS (FD_SETSIZE - 10)
69 #define MAXCLIENTS 1024
72 #define DEFAULT_NXACTS 10 /* default nxacts */
74 int nclients
= 1; /* default number of simulated clients */
75 int nxacts
= 0; /* number of transactions per client */
76 int duration
= 0; /* duration in seconds */
79 * scaling factor. for example, scale = 10 will make 1000000 tuples in
80 * pgbench_accounts table.
85 * fillfactor. for example, fillfactor = 90 will use only 90 percent
86 * space during inserts and leave 10 percent free.
91 * end of configurable parameters
92 *********************************************************************/
96 #define naccounts 100000
100 bool use_log
; /* log transaction latencies to a file */
102 int remains
; /* number of remaining clients */
104 int is_connect
; /* establish connection for each transaction */
108 char *pgoptions
= NULL
;
113 volatile bool timer_exceeded
= false; /* flag from signal handler */
115 /* variable definitions */
118 char *name
; /* variable name */
119 char *value
; /* its value */
122 #define MAX_FILES 128 /* max number of SQL script files allowed */
125 * structures used in custom query mode
130 PGconn
*con
; /* connection handle to DB */
131 int id
; /* client No. */
132 int state
; /* state No. */
133 int cnt
; /* xacts count */
134 int ecnt
; /* error count */
135 int listen
; /* 0 indicates that an async query has been
137 int sleeping
; /* 1 indicates that the client is napping */
138 struct timeval until
; /* napping until */
139 Variable
*variables
; /* array of variable definitions */
141 struct timeval txn_begin
; /* used for measuring latencies */
142 int use_file
; /* index in sql_files for this client */
143 bool prepared
[MAX_FILES
];
147 * queries read from files
149 #define SQL_COMMAND 1
150 #define META_COMMAND 2
153 typedef enum QueryMode
155 QUERY_SIMPLE
, /* simple query */
156 QUERY_EXTENDED
, /* extended query */
157 QUERY_PREPARED
, /* extended query with prepared statements */
161 static QueryMode querymode
= QUERY_SIMPLE
;
162 static const char *QUERYMODE
[] = {"simple", "extended", "prepared"};
166 int type
; /* command type (SQL_COMMAND or META_COMMAND) */
167 int argc
; /* number of commands */
168 char *argv
[MAX_ARGS
]; /* command list */
171 Command
**sql_files
[MAX_FILES
]; /* SQL script files */
172 int num_files
; /* number of script files */
174 /* default scenario */
175 static char *tpc_b
= {
176 "\\set nbranches :scale\n"
177 "\\set ntellers 10 * :scale\n"
178 "\\set naccounts 100000 * :scale\n"
179 "\\setrandom aid 1 :naccounts\n"
180 "\\setrandom bid 1 :nbranches\n"
181 "\\setrandom tid 1 :ntellers\n"
182 "\\setrandom delta -5000 5000\n"
184 "UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
185 "SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
186 "UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;\n"
187 "UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;\n"
188 "INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
193 static char *simple_update
= {
194 "\\set nbranches :scale\n"
195 "\\set ntellers 10 * :scale\n"
196 "\\set naccounts 100000 * :scale\n"
197 "\\setrandom aid 1 :naccounts\n"
198 "\\setrandom bid 1 :nbranches\n"
199 "\\setrandom tid 1 :ntellers\n"
200 "\\setrandom delta -5000 5000\n"
202 "UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
203 "SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
204 "INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
209 static char *select_only
= {
210 "\\set naccounts 100000 * :scale\n"
211 "\\setrandom aid 1 :naccounts\n"
212 "SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
215 /* Connection overhead time */
216 static struct timeval conn_total_time
= {0, 0};
218 /* Function prototypes */
219 static void setalarm(int seconds
);
222 /* Calculate total time */
224 addTime(struct timeval
* t1
, struct timeval
* t2
, struct timeval
* result
)
226 int sec
= t1
->tv_sec
+ t2
->tv_sec
;
227 int usec
= t1
->tv_usec
+ t2
->tv_usec
;
234 result
->tv_sec
= sec
;
235 result
->tv_usec
= usec
;
238 /* Calculate time difference */
240 diffTime(struct timeval
* t1
, struct timeval
* t2
, struct timeval
* result
)
242 int sec
= t1
->tv_sec
- t2
->tv_sec
;
243 int usec
= t1
->tv_usec
- t2
->tv_usec
;
250 result
->tv_sec
= sec
;
251 result
->tv_usec
= usec
;
255 usage(const char *progname
)
257 printf("%s is a benchmarking tool for PostgreSQL.\n\n"
259 " %s [OPTIONS]... [DBNAME]\n"
260 "\nInitialization options:\n"
261 " -i invokes initialization mode\n"
262 " -F NUM fill factor\n"
263 " -s NUM scaling factor\n"
264 "\nBenchmarking options:\n"
265 " -c NUM number of concurrent database clients (default: 1)\n"
266 " -C establish new connection for each transaction\n"
267 " -D VARNAME=VALUE\n"
268 " define variable for use by custom script\n"
269 " -f FILENAME read transaction script from FILENAME\n"
270 " -l write transaction times to log file\n"
271 " -M {simple|extended|prepared}\n"
272 " protocol for submitting queries to server (default: simple)\n"
273 " -n do not run VACUUM before tests\n"
274 " -N do not update tables \"pgbench_tellers\" and \"pgbench_branches\"\n"
275 " -s NUM report this scale factor in output\n"
276 " -S perform SELECT-only transactions\n"
277 " -t NUM number of transactions each client runs (default: 10)\n"
278 " -T NUM duration of benchmark test in seconds\n"
279 " -v vacuum all four standard tables before tests\n"
280 "\nCommon options:\n"
281 " -d print debugging output\n"
282 " -h HOSTNAME database server host or socket directory\n"
283 " -p PORT database server port number\n"
284 " -U USERNAME connect as specified database user\n"
285 " --help show this help, then exit\n"
286 " --version output version information, then exit\n"
288 "Report bugs to <pgsql-bugs@postgresql.org>.\n",
292 /* random number generator: uniform distribution from min to max inclusive */
294 getrand(int min
, int max
)
297 * Odd coding is so that min and max have approximately the same chance of
298 * being selected as do numbers between them.
300 return min
+ (int) (((max
- min
+ 1) * (double) random()) / (MAX_RANDOM_VALUE
+ 1.0));
303 /* call PQexec() and exit() on failure */
305 executeStatement(PGconn
*con
, const char *sql
)
309 res
= PQexec(con
, sql
);
310 if (PQresultStatus(res
) != PGRES_COMMAND_OK
)
312 fprintf(stderr
, "%s", PQerrorMessage(con
));
318 /* set up a connection to the backend */
323 static char *password
= NULL
;
327 * Start the connection. Loop until we have a password if requested by
334 conn
= PQsetdbLogin(pghost
, pgport
, pgoptions
, pgtty
, dbName
,
338 fprintf(stderr
, "Connection to database \"%s\" failed\n",
343 if (PQstatus(conn
) == CONNECTION_BAD
&&
344 PQconnectionNeedsPassword(conn
) &&
348 password
= simple_prompt("Password: ", 100, false);
353 /* check to see that the backend connection was successfully made */
354 if (PQstatus(conn
) == CONNECTION_BAD
)
356 fprintf(stderr
, "Connection to database \"%s\" failed:\n%s",
357 dbName
, PQerrorMessage(conn
));
365 /* throw away response from backend */
367 discard_response(CState
*state
)
373 res
= PQgetResult(state
->con
);
379 /* check to see if the SQL result was good */
381 check(CState
*state
, PGresult
*res
, int n
)
383 CState
*st
= &state
[n
];
385 switch (PQresultStatus(res
))
387 case PGRES_COMMAND_OK
:
388 case PGRES_TUPLES_OK
:
392 fprintf(stderr
, "Client %d aborted in state %d: %s",
393 n
, st
->state
, PQerrorMessage(st
->con
));
394 remains
--; /* I've aborted */
403 compareVariables(const void *v1
, const void *v2
)
405 return strcmp(((const Variable
*) v1
)->name
,
406 ((const Variable
*) v2
)->name
);
410 getVariable(CState
*st
, char *name
)
415 /* On some versions of Solaris, bsearch of zero items dumps core */
416 if (st
->nvariables
<= 0)
420 var
= (Variable
*) bsearch((void *) &key
,
421 (void *) st
->variables
,
432 putVariable(CState
*st
, char *name
, char *value
)
438 /* On some versions of Solaris, bsearch of zero items dumps core */
439 if (st
->nvariables
> 0)
440 var
= (Variable
*) bsearch((void *) &key
,
441 (void *) st
->variables
,
453 newvars
= (Variable
*) realloc(st
->variables
,
454 (st
->nvariables
+ 1) * sizeof(Variable
));
456 newvars
= (Variable
*) malloc(sizeof(Variable
));
461 st
->variables
= newvars
;
463 var
= &newvars
[st
->nvariables
];
468 if ((var
->name
= strdup(name
)) == NULL
469 || (var
->value
= strdup(value
)) == NULL
)
478 qsort((void *) st
->variables
, st
->nvariables
, sizeof(Variable
),
485 if ((val
= strdup(value
)) == NULL
)
496 parseVariable(const char *sql
, int *eaten
)
504 } while (isalnum((unsigned char) sql
[i
]) || sql
[i
] == '_');
511 memcpy(name
, &sql
[1], i
- 1);
519 replaceVariable(char **sql
, char *param
, int len
, char *value
)
521 int valueln
= strlen(value
);
526 size_t offset
= param
- *sql
;
528 tmp
= realloc(*sql
, strlen(*sql
) - len
+ valueln
+ 1);
535 param
= *sql
+ offset
;
539 memmove(param
+ valueln
, param
+ len
, strlen(param
+ len
) + 1);
540 strncpy(param
, value
, valueln
);
542 return param
+ valueln
;
546 assignVariables(CState
*st
, char *sql
)
553 while ((p
= strchr(p
, ':')) != NULL
)
557 name
= parseVariable(p
, &eaten
);
567 val
= getVariable(st
, name
);
575 if ((p
= replaceVariable(&sql
, p
, eaten
, val
)) == NULL
)
583 getQueryParams(CState
*st
, const Command
*command
, const char **params
)
587 for (i
= 0; i
< command
->argc
- 1; i
++)
588 params
[i
] = getVariable(st
, command
->argv
[i
+ 1]);
591 #define MAX_PREPARE_NAME 32
593 preparedStatementName(char *buffer
, int file
, int state
)
595 sprintf(buffer
, "P%d_%d", file
, state
);
599 doCustom(CState
*state
, int n
, int debug
)
602 CState
*st
= &state
[n
];
606 commands
= sql_files
[st
->use_file
];
609 { /* are we sleeping? */
613 gettimeofday(&now
, NULL
);
614 usec
= (st
->until
.tv_sec
- now
.tv_sec
) * 1000000 +
615 st
->until
.tv_usec
- now
.tv_usec
;
617 st
->sleeping
= 0; /* Done sleeping, go ahead with next command */
619 return; /* Still sleeping, nothing to do here */
623 { /* are we receiver? */
624 if (commands
[st
->state
]->type
== SQL_COMMAND
)
627 fprintf(stderr
, "client %d receiving\n", n
);
628 if (!PQconsumeInput(st
->con
))
629 { /* there's something wrong */
630 fprintf(stderr
, "Client %d aborted in state %d. Probably the backend died while processing.\n", n
, st
->state
);
631 remains
--; /* I've aborted */
636 if (PQisBusy(st
->con
))
637 return; /* don't have the whole result yet */
641 * transaction finished: record the time it took in the log
643 if (use_log
&& commands
[st
->state
+ 1] == NULL
)
648 gettimeofday(&now
, NULL
);
649 diff
= (int) (now
.tv_sec
- st
->txn_begin
.tv_sec
) * 1000000.0 +
650 (int) (now
.tv_usec
- st
->txn_begin
.tv_usec
);
652 fprintf(LOGFILE
, "%d %d %.0f %d %ld %ld\n",
653 st
->id
, st
->cnt
, diff
, st
->use_file
,
654 (long) now
.tv_sec
, (long) now
.tv_usec
);
657 if (commands
[st
->state
]->type
== SQL_COMMAND
)
659 res
= PQgetResult(st
->con
);
660 if (check(state
, res
, n
))
666 discard_response(st
);
669 if (commands
[st
->state
+ 1] == NULL
)
678 if ((st
->cnt
>= nxacts
&& duration
<= 0) || timer_exceeded
)
680 remains
--; /* I've done */
690 /* increment state counter */
692 if (commands
[st
->state
] == NULL
)
695 st
->use_file
= getrand(0, num_files
- 1);
696 commands
= sql_files
[st
->use_file
];
706 gettimeofday(&t1
, NULL
);
707 if ((st
->con
= doConnect()) == NULL
)
709 fprintf(stderr
, "Client %d aborted in establishing connection.\n",
711 remains
--; /* I've aborted */
716 gettimeofday(&t2
, NULL
);
717 diffTime(&t2
, &t1
, &t3
);
718 addTime(&conn_total_time
, &t3
, &conn_total_time
);
721 if (use_log
&& st
->state
== 0)
722 gettimeofday(&(st
->txn_begin
), NULL
);
724 if (commands
[st
->state
]->type
== SQL_COMMAND
)
726 const Command
*command
= commands
[st
->state
];
729 if (querymode
== QUERY_SIMPLE
)
733 if ((sql
= strdup(command
->argv
[0])) == NULL
734 || (sql
= assignVariables(st
, sql
)) == NULL
)
736 fprintf(stderr
, "out of memory\n");
742 fprintf(stderr
, "client %d sending %s\n", n
, sql
);
743 r
= PQsendQuery(st
->con
, sql
);
746 else if (querymode
== QUERY_EXTENDED
)
748 const char *sql
= command
->argv
[0];
749 const char *params
[MAX_ARGS
];
751 getQueryParams(st
, command
, params
);
754 fprintf(stderr
, "client %d sending %s\n", n
, sql
);
755 r
= PQsendQueryParams(st
->con
, sql
, command
->argc
- 1,
756 NULL
, params
, NULL
, NULL
, 0);
758 else if (querymode
== QUERY_PREPARED
)
760 char name
[MAX_PREPARE_NAME
];
761 const char *params
[MAX_ARGS
];
763 if (!st
->prepared
[st
->use_file
])
767 for (j
= 0; commands
[j
] != NULL
; j
++)
770 char name
[MAX_PREPARE_NAME
];
772 if (commands
[j
]->type
!= SQL_COMMAND
)
774 preparedStatementName(name
, st
->use_file
, j
);
775 res
= PQprepare(st
->con
, name
,
776 commands
[j
]->argv
[0], commands
[j
]->argc
- 1, NULL
);
777 if (PQresultStatus(res
) != PGRES_COMMAND_OK
)
778 fprintf(stderr
, "%s", PQerrorMessage(st
->con
));
781 st
->prepared
[st
->use_file
] = true;
784 getQueryParams(st
, command
, params
);
785 preparedStatementName(name
, st
->use_file
, st
->state
);
788 fprintf(stderr
, "client %d sending %s\n", n
, name
);
789 r
= PQsendQueryPrepared(st
->con
, name
, command
->argc
- 1,
790 params
, NULL
, NULL
, 0);
792 else /* unknown sql mode */
798 fprintf(stderr
, "client %d cannot send %s\n", n
, command
->argv
[0]);
802 st
->listen
= 1; /* flags that should be listened */
804 else if (commands
[st
->state
]->type
== META_COMMAND
)
806 int argc
= commands
[st
->state
]->argc
,
808 char **argv
= commands
[st
->state
]->argv
;
812 fprintf(stderr
, "client %d executing \\%s", n
, argv
[0]);
813 for (i
= 1; i
< argc
; i
++)
814 fprintf(stderr
, " %s", argv
[i
]);
815 fprintf(stderr
, "\n");
818 if (pg_strcasecmp(argv
[0], "setrandom") == 0)
827 if ((var
= getVariable(st
, argv
[2] + 1)) == NULL
)
829 fprintf(stderr
, "%s: undefined variable %s\n", argv
[0], argv
[2]);
841 fprintf(stderr
, "%s: invalid minimum number %d\n", argv
[0], min
);
849 if ((var
= getVariable(st
, argv
[3] + 1)) == NULL
)
851 fprintf(stderr
, "%s: undefined variable %s\n", argv
[0], argv
[3]);
860 if (max
< min
|| max
> MAX_RANDOM_VALUE
)
862 fprintf(stderr
, "%s: invalid maximum number %d\n", argv
[0], max
);
868 printf("min: %d max: %d random: %d\n", min
, max
, getrand(min
, max
));
870 snprintf(res
, sizeof(res
), "%d", getrand(min
, max
));
872 if (putVariable(st
, argv
[1], res
) == false)
874 fprintf(stderr
, "%s: out of memory\n", argv
[0]);
881 else if (pg_strcasecmp(argv
[0], "set") == 0)
890 if ((var
= getVariable(st
, argv
[2] + 1)) == NULL
)
892 fprintf(stderr
, "%s: undefined variable %s\n", argv
[0], argv
[2]);
899 ope1
= atoi(argv
[2]);
902 snprintf(res
, sizeof(res
), "%d", ope1
);
907 if ((var
= getVariable(st
, argv
[4] + 1)) == NULL
)
909 fprintf(stderr
, "%s: undefined variable %s\n", argv
[0], argv
[4]);
916 ope2
= atoi(argv
[4]);
918 if (strcmp(argv
[3], "+") == 0)
919 snprintf(res
, sizeof(res
), "%d", ope1
+ ope2
);
920 else if (strcmp(argv
[3], "-") == 0)
921 snprintf(res
, sizeof(res
), "%d", ope1
- ope2
);
922 else if (strcmp(argv
[3], "*") == 0)
923 snprintf(res
, sizeof(res
), "%d", ope1
* ope2
);
924 else if (strcmp(argv
[3], "/") == 0)
928 fprintf(stderr
, "%s: division by zero\n", argv
[0]);
932 snprintf(res
, sizeof(res
), "%d", ope1
/ ope2
);
936 fprintf(stderr
, "%s: unsupported operator %s\n", argv
[0], argv
[3]);
942 if (putVariable(st
, argv
[1], res
) == false)
944 fprintf(stderr
, "%s: out of memory\n", argv
[0]);
951 else if (pg_strcasecmp(argv
[0], "sleep") == 0)
959 if ((var
= getVariable(st
, argv
[1] + 1)) == NULL
)
961 fprintf(stderr
, "%s: undefined variable %s\n", argv
[0], argv
[1]);
968 usec
= atoi(argv
[1]);
972 if (pg_strcasecmp(argv
[2], "ms") == 0)
974 else if (pg_strcasecmp(argv
[2], "s") == 0)
980 gettimeofday(&now
, NULL
);
981 st
->until
.tv_sec
= now
.tv_sec
+ (now
.tv_usec
+ usec
) / 1000000;
982 st
->until
.tv_usec
= (now
.tv_usec
+ usec
) % 1000000;
992 /* discard connections */
994 disconnect_all(CState
*state
)
998 for (i
= 0; i
< nclients
; i
++)
1001 PQfinish(state
[i
].con
);
1005 /* create tables and setup data */
1010 * Note: TPC-B requires at least 100 bytes per row, and the "filler"
1011 * fields in these table declarations were intended to comply with that.
1012 * But because they default to NULLs, they don't actually take any space.
1013 * We could fix that by giving them non-null default values. However, that
1014 * would completely break comparability of pgbench results with prior
1015 * versions. Since pgbench has never pretended to be fully TPC-B
1016 * compliant anyway, we stick with the historical behavior.
1018 static char *DDLs
[] = {
1019 "drop table if exists pgbench_branches",
1020 "create table pgbench_branches(bid int not null,bbalance int,filler char(88)) with (fillfactor=%d)",
1021 "drop table if exists pgbench_tellers",
1022 "create table pgbench_tellers(tid int not null,bid int,tbalance int,filler char(84)) with (fillfactor=%d)",
1023 "drop table if exists pgbench_accounts",
1024 "create table pgbench_accounts(aid int not null,bid int,abalance int,filler char(84)) with (fillfactor=%d)",
1025 "drop table if exists pgbench_history",
1026 "create table pgbench_history(tid int,bid int,aid int,delta int,mtime timestamp,filler char(22))"
1028 static char *DDLAFTERs
[] = {
1029 "alter table pgbench_branches add primary key (bid)",
1030 "alter table pgbench_tellers add primary key (tid)",
1031 "alter table pgbench_accounts add primary key (aid)"
1039 if ((con
= doConnect()) == NULL
)
1042 for (i
= 0; i
< lengthof(DDLs
); i
++)
1045 * set fillfactor for branches, tellers and accounts tables
1047 if ((strstr(DDLs
[i
], "create table pgbench_branches") == DDLs
[i
]) ||
1048 (strstr(DDLs
[i
], "create table pgbench_tellers") == DDLs
[i
]) ||
1049 (strstr(DDLs
[i
], "create table pgbench_accounts") == DDLs
[i
]))
1053 snprintf(ddl_stmt
, 128, DDLs
[i
], fillfactor
);
1054 executeStatement(con
, ddl_stmt
);
1058 executeStatement(con
, DDLs
[i
]);
1061 executeStatement(con
, "begin");
1063 for (i
= 0; i
< nbranches
* scale
; i
++)
1065 snprintf(sql
, 256, "insert into pgbench_branches(bid,bbalance) values(%d,0)", i
+ 1);
1066 executeStatement(con
, sql
);
1069 for (i
= 0; i
< ntellers
* scale
; i
++)
1071 snprintf(sql
, 256, "insert into pgbench_tellers(tid,bid,tbalance) values (%d,%d,0)"
1072 ,i
+ 1, i
/ ntellers
+ 1);
1073 executeStatement(con
, sql
);
1076 executeStatement(con
, "commit");
1079 * fill the pgbench_accounts table with some data
1081 fprintf(stderr
, "creating tables...\n");
1083 executeStatement(con
, "begin");
1084 executeStatement(con
, "truncate pgbench_accounts");
1086 res
= PQexec(con
, "copy pgbench_accounts from stdin");
1087 if (PQresultStatus(res
) != PGRES_COPY_IN
)
1089 fprintf(stderr
, "%s", PQerrorMessage(con
));
1094 for (i
= 0; i
< naccounts
* scale
; i
++)
1098 snprintf(sql
, 256, "%d\t%d\t%d\t\n", j
, i
/ naccounts
+ 1, 0);
1099 if (PQputline(con
, sql
))
1101 fprintf(stderr
, "PQputline failed\n");
1106 fprintf(stderr
, "%d tuples done.\n", j
);
1108 if (PQputline(con
, "\\.\n"))
1110 fprintf(stderr
, "very last PQputline failed\n");
1115 fprintf(stderr
, "PQendcopy failed\n");
1118 executeStatement(con
, "commit");
1123 fprintf(stderr
, "set primary key...\n");
1124 for (i
= 0; i
< lengthof(DDLAFTERs
); i
++)
1125 executeStatement(con
, DDLAFTERs
[i
]);
1128 fprintf(stderr
, "vacuum...");
1129 executeStatement(con
, "vacuum analyze pgbench_branches");
1130 executeStatement(con
, "vacuum analyze pgbench_tellers");
1131 executeStatement(con
, "vacuum analyze pgbench_accounts");
1132 executeStatement(con
, "vacuum analyze pgbench_history");
1134 fprintf(stderr
, "done.\n");
1139 * Parse the raw sql and replace :param to $n.
1142 parseQuery(Command
*cmd
, const char *raw_sql
)
1147 sql
= strdup(raw_sql
);
1153 while ((p
= strchr(p
, ':')) != NULL
)
1159 name
= parseVariable(p
, &eaten
);
1169 if (cmd
->argc
>= MAX_ARGS
)
1171 fprintf(stderr
, "statement has too many arguments (maximum is %d): %s\n", MAX_ARGS
- 1, raw_sql
);
1175 sprintf(var
, "$%d", cmd
->argc
);
1176 if ((p
= replaceVariable(&sql
, p
, eaten
, var
)) == NULL
)
1179 cmd
->argv
[cmd
->argc
] = name
;
1188 process_commands(char *buf
)
1190 const char delim
[] = " \f\n\r\t\v";
1192 Command
*my_commands
;
1197 if ((p
= strchr(buf
, '\n')) != NULL
)
1201 while (isspace((unsigned char) *p
))
1204 if (*p
== '\0' || strncmp(p
, "--", 2) == 0)
1209 my_commands
= (Command
*) malloc(sizeof(Command
));
1210 if (my_commands
== NULL
)
1215 my_commands
->argc
= 0;
1219 my_commands
->type
= META_COMMAND
;
1222 tok
= strtok(++p
, delim
);
1226 if ((my_commands
->argv
[j
] = strdup(tok
)) == NULL
)
1229 my_commands
->argc
++;
1232 tok
= strtok(NULL
, delim
);
1235 if (pg_strcasecmp(my_commands
->argv
[0], "setrandom") == 0)
1237 if (my_commands
->argc
< 4)
1239 fprintf(stderr
, "%s: missing argument\n", my_commands
->argv
[0]);
1243 for (j
= 4; j
< my_commands
->argc
; j
++)
1244 fprintf(stderr
, "%s: extra argument \"%s\" ignored\n",
1245 my_commands
->argv
[0], my_commands
->argv
[j
]);
1247 else if (pg_strcasecmp(my_commands
->argv
[0], "set") == 0)
1249 if (my_commands
->argc
< 3)
1251 fprintf(stderr
, "%s: missing argument\n", my_commands
->argv
[0]);
1255 for (j
= my_commands
->argc
< 5 ? 3 : 5; j
< my_commands
->argc
; j
++)
1256 fprintf(stderr
, "%s: extra argument \"%s\" ignored\n",
1257 my_commands
->argv
[0], my_commands
->argv
[j
]);
1259 else if (pg_strcasecmp(my_commands
->argv
[0], "sleep") == 0)
1261 if (my_commands
->argc
< 2)
1263 fprintf(stderr
, "%s: missing argument\n", my_commands
->argv
[0]);
1267 if (my_commands
->argc
>= 3)
1269 if (pg_strcasecmp(my_commands
->argv
[2], "us") != 0 &&
1270 pg_strcasecmp(my_commands
->argv
[2], "ms") != 0 &&
1271 pg_strcasecmp(my_commands
->argv
[2], "s"))
1273 fprintf(stderr
, "%s: unknown time unit '%s' - must be us, ms or s\n",
1274 my_commands
->argv
[0], my_commands
->argv
[2]);
1279 for (j
= 3; j
< my_commands
->argc
; j
++)
1280 fprintf(stderr
, "%s: extra argument \"%s\" ignored\n",
1281 my_commands
->argv
[0], my_commands
->argv
[j
]);
1285 fprintf(stderr
, "Invalid command %s\n", my_commands
->argv
[0]);
1291 my_commands
->type
= SQL_COMMAND
;
1296 if ((my_commands
->argv
[0] = strdup(p
)) == NULL
)
1298 my_commands
->argc
++;
1300 case QUERY_EXTENDED
:
1301 case QUERY_PREPARED
:
1302 if (!parseQuery(my_commands
, p
))
1314 process_file(char *filename
)
1316 #define COMMANDS_ALLOC_NUM 128
1318 Command
**my_commands
;
1324 if (num_files
>= MAX_FILES
)
1326 fprintf(stderr
, "Up to only %d SQL files are allowed\n", MAX_FILES
);
1330 alloc_num
= COMMANDS_ALLOC_NUM
;
1331 my_commands
= (Command
**) malloc(sizeof(Command
*) * alloc_num
);
1332 if (my_commands
== NULL
)
1335 if (strcmp(filename
, "-") == 0)
1337 else if ((fd
= fopen(filename
, "r")) == NULL
)
1339 fprintf(stderr
, "%s: %s\n", filename
, strerror(errno
));
1345 while (fgets(buf
, sizeof(buf
), fd
) != NULL
)
1351 while (isspace((unsigned char) buf
[i
]))
1354 if (buf
[i
] != '\0' && strncmp(&buf
[i
], "--", 2) != 0)
1356 commands
= process_commands(&buf
[i
]);
1357 if (commands
== NULL
)
1366 my_commands
[lineno
] = commands
;
1369 if (lineno
>= alloc_num
)
1371 alloc_num
+= COMMANDS_ALLOC_NUM
;
1372 my_commands
= realloc(my_commands
, sizeof(Command
*) * alloc_num
);
1373 if (my_commands
== NULL
)
1382 my_commands
[lineno
] = NULL
;
1384 sql_files
[num_files
++] = my_commands
;
1390 process_builtin(char *tb
)
1392 #define COMMANDS_ALLOC_NUM 128
1394 Command
**my_commands
;
1402 alloc_num
= COMMANDS_ALLOC_NUM
;
1403 my_commands
= (Command
**) malloc(sizeof(Command
*) * alloc_num
);
1404 if (my_commands
== NULL
)
1415 while (*tb
&& *tb
!= '\n')
1426 commands
= process_commands(buf
);
1427 if (commands
== NULL
)
1432 my_commands
[lineno
] = commands
;
1435 if (lineno
>= alloc_num
)
1437 alloc_num
+= COMMANDS_ALLOC_NUM
;
1438 my_commands
= realloc(my_commands
, sizeof(Command
*) * alloc_num
);
1439 if (my_commands
== NULL
)
1446 my_commands
[lineno
] = NULL
;
1451 /* print out results */
1454 int ttype
, CState
*state
,
1455 struct timeval
* start_time
, struct timeval
* end_time
)
1460 int normal_xacts
= 0;
1463 for (i
= 0; i
< nclients
; i
++)
1464 normal_xacts
+= state
[i
].cnt
;
1466 t1
= (end_time
->tv_sec
- start_time
->tv_sec
) * 1000000.0 + (end_time
->tv_usec
- start_time
->tv_usec
);
1467 t1
= normal_xacts
* 1000000.0 / t1
;
1469 t2
= (end_time
->tv_sec
- start_time
->tv_sec
- conn_total_time
.tv_sec
) * 1000000.0 +
1470 (end_time
->tv_usec
- start_time
->tv_usec
- conn_total_time
.tv_usec
);
1471 t2
= normal_xacts
* 1000000.0 / t2
;
1474 s
= "TPC-B (sort of)";
1475 else if (ttype
== 2)
1476 s
= "Update only pgbench_accounts";
1477 else if (ttype
== 1)
1482 printf("transaction type: %s\n", s
);
1483 printf("scaling factor: %d\n", scale
);
1484 printf("query mode: %s\n", QUERYMODE
[querymode
]);
1485 printf("number of clients: %d\n", nclients
);
1488 printf("number of transactions per client: %d\n", nxacts
);
1489 printf("number of transactions actually processed: %d/%d\n",
1490 normal_xacts
, nxacts
* nclients
);
1494 printf("duration: %d s\n", duration
);
1495 printf("number of transactions actually processed: %d\n",
1498 printf("tps = %f (including connections establishing)\n", t1
);
1499 printf("tps = %f (excluding connections establishing)\n", t2
);
1504 main(int argc
, char **argv
)
1507 int is_init_mode
= 0; /* initialize mode? */
1508 int is_no_vacuum
= 0; /* no vacuum at all before testing? */
1509 int do_vacuum_accounts
= 0; /* do vacuum accounts before testing? */
1510 int debug
= 0; /* debug flag */
1511 int ttype
= 0; /* transaction type. 0: TPC-B, 1: SELECT only,
1512 * 2: skip update of branches and tellers */
1513 char *filename
= NULL
;
1514 bool scale_given
= false;
1516 CState
*state
; /* status of clients */
1518 struct timeval start_time
; /* start up time */
1519 struct timeval end_time
; /* end time */
1524 int nsocks
; /* return from select(2) */
1525 int maxsock
; /* max socket number to be waited */
1527 struct timeval timeout
;
1530 #ifdef HAVE_GETRLIMIT
1540 const char *progname
;
1542 progname
= get_progname(argv
[0]);
1546 if (strcmp(argv
[1], "--help") == 0 || strcmp(argv
[1], "-?") == 0)
1551 if (strcmp(argv
[1], "--version") == 0 || strcmp(argv
[1], "-V") == 0)
1553 puts("pgbench (PostgreSQL) " PG_VERSION
);
1559 /* stderr is buffered on Win32. */
1560 setvbuf(stderr
, NULL
, _IONBF
, 0);
1563 if ((env
= getenv("PGHOST")) != NULL
&& *env
!= '\0')
1565 if ((env
= getenv("PGPORT")) != NULL
&& *env
!= '\0')
1567 else if ((env
= getenv("PGUSER")) != NULL
&& *env
!= '\0')
1570 state
= (CState
*) malloc(sizeof(CState
));
1573 fprintf(stderr
, "Couldn't allocate memory for state\n");
1577 memset(state
, 0, sizeof(*state
));
1579 while ((c
= getopt(argc
, argv
, "ih:nvp:dSNc:Cs:t:T:U:lf:D:F:M:")) != -1)
1593 do_vacuum_accounts
++;
1608 nclients
= atoi(optarg
);
1609 if (nclients
<= 0 || nclients
> MAXCLIENTS
)
1611 fprintf(stderr
, "invalid number of clients: %d\n", nclients
);
1614 #ifdef HAVE_GETRLIMIT
1615 #ifdef RLIMIT_NOFILE /* most platforms use RLIMIT_NOFILE */
1616 if (getrlimit(RLIMIT_NOFILE
, &rlim
) == -1)
1617 #else /* but BSD doesn't ... */
1618 if (getrlimit(RLIMIT_OFILE
, &rlim
) == -1)
1619 #endif /* RLIMIT_NOFILE */
1621 fprintf(stderr
, "getrlimit failed: %s\n", strerror(errno
));
1624 if (rlim
.rlim_cur
<= (nclients
+ 2))
1626 fprintf(stderr
, "You need at least %d open files but you are only allowed to use %ld.\n", nclients
+ 2, (long) rlim
.rlim_cur
);
1627 fprintf(stderr
, "Use limit/ulimit to increase the limit before using pgbench.\n");
1630 #endif /* HAVE_GETRLIMIT */
1637 scale
= atoi(optarg
);
1640 fprintf(stderr
, "invalid scaling factor: %d\n", scale
);
1647 fprintf(stderr
, "specify either a number of transactions (-t) or a duration (-T), not both.\n");
1650 nxacts
= atoi(optarg
);
1653 fprintf(stderr
, "invalid number of transactions: %d\n", nxacts
);
1660 fprintf(stderr
, "specify either a number of transactions (-t) or a duration (-T), not both.\n");
1663 duration
= atoi(optarg
);
1666 fprintf(stderr
, "invalid duration: %d\n", duration
);
1679 if (process_file(filename
) == false || *sql_files
[num_files
- 1] == NULL
)
1686 if ((p
= strchr(optarg
, '=')) == NULL
|| p
== optarg
|| *(p
+ 1) == '\0')
1688 fprintf(stderr
, "invalid variable definition: %s\n", optarg
);
1693 if (putVariable(&state
[0], optarg
, p
) == false)
1695 fprintf(stderr
, "Couldn't allocate memory for variable\n");
1701 fillfactor
= atoi(optarg
);
1702 if ((fillfactor
< 10) || (fillfactor
> 100))
1704 fprintf(stderr
, "invalid fillfactor: %d\n", fillfactor
);
1711 fprintf(stderr
, "query mode (-M) should be specifiled before transaction scripts (-f)\n");
1714 for (querymode
= 0; querymode
< NUM_QUERYMODE
; querymode
++)
1715 if (strcmp(optarg
, QUERYMODE
[querymode
]) == 0)
1717 if (querymode
>= NUM_QUERYMODE
)
1719 fprintf(stderr
, "invalid query mode (-M): %s\n", optarg
);
1724 fprintf(stderr
, _("Try \"%s --help\" for more information.\n"), progname
);
1731 dbName
= argv
[optind
];
1734 if ((env
= getenv("PGDATABASE")) != NULL
&& *env
!= '\0')
1736 else if (login
!= NULL
&& *login
!= '\0')
1748 /* Use DEFAULT_NXACTS if neither nxacts nor duration is specified. */
1749 if (nxacts
<= 0 && duration
<= 0)
1750 nxacts
= DEFAULT_NXACTS
;
1756 state
= (CState
*) realloc(state
, sizeof(CState
) * nclients
);
1759 fprintf(stderr
, "Couldn't allocate memory for state\n");
1763 memset(state
+ 1, 0, sizeof(*state
) * (nclients
- 1));
1765 /* copy any -D switch values to all clients */
1766 for (i
= 1; i
< nclients
; i
++)
1770 for (j
= 0; j
< state
[0].nvariables
; j
++)
1772 if (putVariable(&state
[i
], state
[0].variables
[j
].name
, state
[0].variables
[j
].value
) == false)
1774 fprintf(stderr
, "Couldn't allocate memory for variable\n");
1785 snprintf(logpath
, 64, "pgbench_log.%d", (int) getpid());
1786 LOGFILE
= fopen(logpath
, "w");
1788 if (LOGFILE
== NULL
)
1790 fprintf(stderr
, "Couldn't open logfile \"%s\": %s", logpath
, strerror(errno
));
1798 printf("pghost: %s pgport: %s nclients: %d nxacts: %d dbName: %s\n",
1799 pghost
, pgport
, nclients
, nxacts
, dbName
);
1801 printf("pghost: %s pgport: %s nclients: %d duration: %d dbName: %s\n",
1802 pghost
, pgport
, nclients
, duration
, dbName
);
1805 /* opening connection... */
1810 if (PQstatus(con
) == CONNECTION_BAD
)
1812 fprintf(stderr
, "Connection to database '%s' failed.\n", dbName
);
1813 fprintf(stderr
, "%s", PQerrorMessage(con
));
1820 * get the scaling factor that should be same as count(*) from
1821 * pgbench_branches if this is not a custom query
1823 res
= PQexec(con
, "select count(*) from pgbench_branches");
1824 if (PQresultStatus(res
) != PGRES_TUPLES_OK
)
1826 fprintf(stderr
, "%s", PQerrorMessage(con
));
1829 scale
= atoi(PQgetvalue(res
, 0, 0));
1832 fprintf(stderr
, "count(*) from pgbench_branches invalid (%d)\n", scale
);
1837 /* warn if we override user-given -s switch */
1840 "Scale option ignored, using pgbench_branches table count = %d\n",
1845 * :scale variables normally get -s or database scale, but don't override
1846 * an explicit -D switch
1848 if (getVariable(&state
[0], "scale") == NULL
)
1850 snprintf(val
, sizeof(val
), "%d", scale
);
1851 for (i
= 0; i
< nclients
; i
++)
1853 if (putVariable(&state
[i
], "scale", val
) == false)
1855 fprintf(stderr
, "Couldn't allocate memory for variable\n");
1863 fprintf(stderr
, "starting vacuum...");
1864 executeStatement(con
, "vacuum pgbench_branches");
1865 executeStatement(con
, "vacuum pgbench_tellers");
1866 executeStatement(con
, "truncate pgbench_history");
1867 fprintf(stderr
, "end.\n");
1869 if (do_vacuum_accounts
)
1871 fprintf(stderr
, "starting vacuum pgbench_accounts...");
1872 executeStatement(con
, "vacuum analyze pgbench_accounts");
1873 fprintf(stderr
, "end.\n");
1878 /* set random seed */
1879 gettimeofday(&start_time
, NULL
);
1880 srandom((unsigned int) start_time
.tv_usec
);
1882 /* get start up time */
1883 gettimeofday(&start_time
, NULL
);
1885 /* set alarm if duration is specified. */
1889 if (is_connect
== 0)
1894 /* make connections to the database */
1895 for (i
= 0; i
< nclients
; i
++)
1898 if ((state
[i
].con
= doConnect()) == NULL
)
1901 /* time after connections set up */
1902 gettimeofday(&now
, NULL
);
1903 diffTime(&now
, &start_time
, &t
);
1904 addTime(&conn_total_time
, &t
, &conn_total_time
);
1907 /* process bultin SQL scripts */
1911 sql_files
[0] = process_builtin(tpc_b
);
1916 sql_files
[0] = process_builtin(select_only
);
1921 sql_files
[0] = process_builtin(simple_update
);
1929 /* send start up queries in async manner */
1930 for (i
= 0; i
< nclients
; i
++)
1932 Command
**commands
= sql_files
[state
[i
].use_file
];
1933 int prev_ecnt
= state
[i
].ecnt
;
1935 state
[i
].use_file
= getrand(0, num_files
- 1);
1936 doCustom(state
, i
, debug
);
1938 if (state
[i
].ecnt
> prev_ecnt
&& commands
[state
[i
].state
]->type
== META_COMMAND
)
1940 fprintf(stderr
, "Client %d aborted in state %d. Execution meta-command failed.\n", i
, state
[i
].state
);
1941 remains
--; /* I've aborted */
1942 PQfinish(state
[i
].con
);
1943 state
[i
].con
= NULL
;
1951 disconnect_all(state
);
1953 gettimeofday(&end_time
, NULL
);
1954 printResults(ttype
, state
, &start_time
, &end_time
);
1960 FD_ZERO(&input_mask
);
1964 for (i
= 0; i
< nclients
; i
++)
1966 Command
**commands
= sql_files
[state
[i
].use_file
];
1968 if (state
[i
].sleeping
)
1971 int sock
= PQsocket(state
[i
].con
);
1975 gettimeofday(&now
, NULL
);
1979 this_usec
= (state
[i
].until
.tv_sec
- now
.tv_sec
) * 1000000 +
1980 state
[i
].until
.tv_usec
- now
.tv_usec
;
1982 if (this_usec
> 0 && (min_usec
== 0 || this_usec
< min_usec
))
1983 min_usec
= this_usec
;
1985 FD_SET (sock
, &input_mask
);
1990 else if (state
[i
].con
&& commands
[state
[i
].state
]->type
!= META_COMMAND
)
1992 int sock
= PQsocket(state
[i
].con
);
1996 disconnect_all(state
);
1999 FD_SET (sock
, &input_mask
);
2010 timeout
.tv_sec
= min_usec
/ 1000000;
2011 timeout
.tv_usec
= min_usec
% 1000000;
2013 nsocks
= select(maxsock
+ 1, &input_mask
, (fd_set
*) NULL
,
2014 (fd_set
*) NULL
, &timeout
);
2017 nsocks
= select(maxsock
+ 1, &input_mask
, (fd_set
*) NULL
,
2018 (fd_set
*) NULL
, (struct timeval
*) NULL
);
2023 /* must be something wrong */
2024 disconnect_all(state
);
2025 fprintf(stderr
, "select failed: %s\n", strerror(errno
));
2029 else if (nsocks
== 0)
2031 fprintf(stderr
, "select timeout\n");
2032 for (i
= 0; i
< nclients
; i
++)
2034 fprintf(stderr
, "client %d:state %d cnt %d ecnt %d listen %d\n",
2035 i
, state
[i
].state
, state
[i
].cnt
, state
[i
].ecnt
, state
[i
].listen
);
2042 /* ok, backend returns reply */
2043 for (i
= 0; i
< nclients
; i
++)
2045 Command
**commands
= sql_files
[state
[i
].use_file
];
2046 int prev_ecnt
= state
[i
].ecnt
;
2048 if (state
[i
].con
&& (FD_ISSET(PQsocket(state
[i
].con
), &input_mask
)
2049 || commands
[state
[i
].state
]->type
== META_COMMAND
))
2051 doCustom(state
, i
, debug
);
2054 if (state
[i
].ecnt
> prev_ecnt
&& commands
[state
[i
].state
]->type
== META_COMMAND
)
2056 fprintf(stderr
, "Client %d aborted in state %d. Execution of meta-command failed.\n", i
, state
[i
].state
);
2057 remains
--; /* I've aborted */
2058 PQfinish(state
[i
].con
);
2059 state
[i
].con
= NULL
;
2067 * Support for duration option: set timer_exceeded after so many seconds.
2073 handle_sig_alarm(SIGNAL_ARGS
)
2075 timer_exceeded
= true;
2079 setalarm(int seconds
)
2081 pqsignal(SIGALRM
, handle_sig_alarm
);
2086 static VOID CALLBACK
2087 win32_timer_callback(PVOID lpParameter
, BOOLEAN TimerOrWaitFired
)
2089 timer_exceeded
= true;
2093 setalarm(int seconds
)
2098 /* This function will be called at most once, so we can cheat a bit. */
2099 queue
= CreateTimerQueue();
2100 if (seconds
> ((DWORD
) -1) / 1000 ||
2101 !CreateTimerQueueTimer(&timer
, queue
,
2102 win32_timer_callback
, NULL
, seconds
* 1000, 0,
2103 WT_EXECUTEINTIMERTHREAD
| WT_EXECUTEONLYONCE
))
2105 fprintf(stderr
, "Failed to set timer\n");