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
;
233 result
->tv_sec
= sec
;
234 result
->tv_usec
= usec
;
237 /* Calculate time difference */
239 diffTime(struct timeval
*t1
, struct timeval
*t2
, struct timeval
*result
)
241 int sec
= t1
->tv_sec
- t2
->tv_sec
;
242 int usec
= t1
->tv_usec
- t2
->tv_usec
;
248 result
->tv_sec
= sec
;
249 result
->tv_usec
= usec
;
253 usage(const char *progname
)
255 printf("%s is a benchmarking tool for PostgreSQL.\n\n"
257 " %s [OPTIONS]... [DBNAME]\n"
258 "\nInitialization options:\n"
259 " -i invokes initialization mode\n"
260 " -F NUM fill factor\n"
261 " -s NUM scaling factor\n"
262 "\nBenchmarking options:\n"
263 " -c NUM number of concurrent database clients (default: 1)\n"
264 " -C establish new connection for each transaction\n"
265 " -D VARNAME=VALUE\n"
266 " define variable for use by custom script\n"
267 " -f FILENAME read transaction script from FILENAME\n"
268 " -l write transaction times to log file\n"
269 " -M {simple|extended|prepared}\n"
270 " protocol for submitting queries to server (default: simple)\n"
271 " -n do not run VACUUM before tests\n"
272 " -N do not update tables \"pgbench_tellers\" and \"pgbench_branches\"\n"
273 " -s NUM report this scale factor in output\n"
274 " -S perform SELECT-only transactions\n"
275 " -t NUM number of transactions each client runs (default: 10)\n"
276 " -T NUM duration of benchmark test in seconds\n"
277 " -v vacuum all four standard tables before tests\n"
278 "\nCommon options:\n"
279 " -d print debugging output\n"
280 " -h HOSTNAME database server host or socket directory\n"
281 " -p PORT database server port number\n"
282 " -U USERNAME connect as specified database user\n"
283 " --help show this help, then exit\n"
284 " --version output version information, then exit\n"
286 "Report bugs to <pgsql-bugs@postgresql.org>.\n",
290 /* random number generator: uniform distribution from min to max inclusive */
292 getrand(int min
, int max
)
295 * Odd coding is so that min and max have approximately the same chance of
296 * being selected as do numbers between them.
298 return min
+ (int) (((max
- min
+ 1) * (double) random()) / (MAX_RANDOM_VALUE
+ 1.0));
301 /* call PQexec() and exit() on failure */
303 executeStatement(PGconn
*con
, const char *sql
)
307 res
= PQexec(con
, sql
);
308 if (PQresultStatus(res
) != PGRES_COMMAND_OK
)
310 fprintf(stderr
, "%s", PQerrorMessage(con
));
316 /* set up a connection to the backend */
321 static char *password
= NULL
;
325 * Start the connection. Loop until we have a password if requested by
332 conn
= PQsetdbLogin(pghost
, pgport
, pgoptions
, pgtty
, dbName
,
336 fprintf(stderr
, "Connection to database \"%s\" failed\n",
341 if (PQstatus(conn
) == CONNECTION_BAD
&&
342 PQconnectionNeedsPassword(conn
) &&
346 password
= simple_prompt("Password: ", 100, false);
351 /* check to see that the backend connection was successfully made */
352 if (PQstatus(conn
) == CONNECTION_BAD
)
354 fprintf(stderr
, "Connection to database \"%s\" failed:\n%s",
355 dbName
, PQerrorMessage(conn
));
363 /* throw away response from backend */
365 discard_response(CState
* state
)
371 res
= PQgetResult(state
->con
);
377 /* check to see if the SQL result was good */
379 check(CState
* state
, PGresult
*res
, int n
)
381 CState
*st
= &state
[n
];
383 switch (PQresultStatus(res
))
385 case PGRES_COMMAND_OK
:
386 case PGRES_TUPLES_OK
:
390 fprintf(stderr
, "Client %d aborted in state %d: %s",
391 n
, st
->state
, PQerrorMessage(st
->con
));
392 remains
--; /* I've aborted */
401 compareVariables(const void *v1
, const void *v2
)
403 return strcmp(((const Variable
*) v1
)->name
,
404 ((const Variable
*) v2
)->name
);
408 getVariable(CState
* st
, char *name
)
413 /* On some versions of Solaris, bsearch of zero items dumps core */
414 if (st
->nvariables
<= 0)
418 var
= (Variable
*) bsearch((void *) &key
,
419 (void *) st
->variables
,
430 putVariable(CState
* st
, char *name
, char *value
)
436 /* On some versions of Solaris, bsearch of zero items dumps core */
437 if (st
->nvariables
> 0)
438 var
= (Variable
*) bsearch((void *) &key
,
439 (void *) st
->variables
,
451 newvars
= (Variable
*) realloc(st
->variables
,
452 (st
->nvariables
+ 1) * sizeof(Variable
));
454 newvars
= (Variable
*) malloc(sizeof(Variable
));
459 st
->variables
= newvars
;
461 var
= &newvars
[st
->nvariables
];
466 if ((var
->name
= strdup(name
)) == NULL
467 || (var
->value
= strdup(value
)) == NULL
)
476 qsort((void *) st
->variables
, st
->nvariables
, sizeof(Variable
),
483 if ((val
= strdup(value
)) == NULL
)
494 parseVariable(const char *sql
, int *eaten
)
502 } while (isalnum((unsigned char) sql
[i
]) || sql
[i
] == '_');
509 memcpy(name
, &sql
[1], i
- 1);
517 replaceVariable(char **sql
, char *param
, int len
, char *value
)
519 int valueln
= strlen(value
);
524 size_t offset
= param
- *sql
;
526 tmp
= realloc(*sql
, strlen(*sql
) - len
+ valueln
+ 1);
533 param
= *sql
+ offset
;
537 memmove(param
+ valueln
, param
+ len
, strlen(param
+ len
) + 1);
538 strncpy(param
, value
, valueln
);
540 return param
+ valueln
;
544 assignVariables(CState
* st
, char *sql
)
551 while ((p
= strchr(p
, ':')) != NULL
)
555 name
= parseVariable(p
, &eaten
);
558 while (*p
== ':') { p
++; }
562 val
= getVariable(st
, name
);
570 if ((p
= replaceVariable(&sql
, p
, eaten
, val
)) == NULL
)
578 getQueryParams(CState
*st
, const Command
*command
, const char **params
)
582 for (i
= 0; i
< command
->argc
- 1; i
++)
583 params
[i
] = getVariable(st
, command
->argv
[i
+1]);
586 #define MAX_PREPARE_NAME 32
588 preparedStatementName(char *buffer
, int file
, int state
)
590 sprintf(buffer
, "P%d_%d", file
, state
);
594 doCustom(CState
* state
, int n
, int debug
)
597 CState
*st
= &state
[n
];
601 commands
= sql_files
[st
->use_file
];
604 { /* are we sleeping? */
608 gettimeofday(&now
, NULL
);
609 usec
= (st
->until
.tv_sec
- now
.tv_sec
) * 1000000 +
610 st
->until
.tv_usec
- now
.tv_usec
;
612 st
->sleeping
= 0; /* Done sleeping, go ahead with next command */
614 return; /* Still sleeping, nothing to do here */
618 { /* are we receiver? */
619 if (commands
[st
->state
]->type
== SQL_COMMAND
)
622 fprintf(stderr
, "client %d receiving\n", n
);
623 if (!PQconsumeInput(st
->con
))
624 { /* there's something wrong */
625 fprintf(stderr
, "Client %d aborted in state %d. Probably the backend died while processing.\n", n
, st
->state
);
626 remains
--; /* I've aborted */
631 if (PQisBusy(st
->con
))
632 return; /* don't have the whole result yet */
636 * transaction finished: record the time it took in the log
638 if (use_log
&& commands
[st
->state
+ 1] == NULL
)
643 gettimeofday(&now
, NULL
);
644 diff
= (int) (now
.tv_sec
- st
->txn_begin
.tv_sec
) * 1000000.0 +
645 (int) (now
.tv_usec
- st
->txn_begin
.tv_usec
);
647 fprintf(LOGFILE
, "%d %d %.0f %d %ld %ld\n",
648 st
->id
, st
->cnt
, diff
, st
->use_file
,
649 (long) now
.tv_sec
, (long) now
.tv_usec
);
652 if (commands
[st
->state
]->type
== SQL_COMMAND
)
654 res
= PQgetResult(st
->con
);
655 if (check(state
, res
, n
))
661 discard_response(st
);
664 if (commands
[st
->state
+ 1] == NULL
)
673 if ((st
->cnt
>= nxacts
&& duration
<= 0) || timer_exceeded
)
675 remains
--; /* I've done */
685 /* increment state counter */
687 if (commands
[st
->state
] == NULL
)
690 st
->use_file
= getrand(0, num_files
- 1);
691 commands
= sql_files
[st
->use_file
];
697 struct timeval t1
, t2
, t3
;
699 gettimeofday(&t1
, NULL
);
700 if ((st
->con
= doConnect()) == NULL
)
702 fprintf(stderr
, "Client %d aborted in establishing connection.\n",
704 remains
--; /* I've aborted */
709 gettimeofday(&t2
, NULL
);
710 diffTime(&t2
, &t1
, &t3
);
711 addTime(&conn_total_time
, &t3
, &conn_total_time
);
714 if (use_log
&& st
->state
== 0)
715 gettimeofday(&(st
->txn_begin
), NULL
);
717 if (commands
[st
->state
]->type
== SQL_COMMAND
)
719 const Command
*command
= commands
[st
->state
];
722 if (querymode
== QUERY_SIMPLE
)
726 if ((sql
= strdup(command
->argv
[0])) == NULL
727 || (sql
= assignVariables(st
, sql
)) == NULL
)
729 fprintf(stderr
, "out of memory\n");
735 fprintf(stderr
, "client %d sending %s\n", n
, sql
);
736 r
= PQsendQuery(st
->con
, sql
);
739 else if (querymode
== QUERY_EXTENDED
)
741 const char *sql
= command
->argv
[0];
742 const char *params
[MAX_ARGS
];
744 getQueryParams(st
, command
, params
);
747 fprintf(stderr
, "client %d sending %s\n", n
, sql
);
748 r
= PQsendQueryParams(st
->con
, sql
, command
->argc
- 1,
749 NULL
, params
, NULL
, NULL
, 0);
751 else if (querymode
== QUERY_PREPARED
)
753 char name
[MAX_PREPARE_NAME
];
754 const char *params
[MAX_ARGS
];
756 if (!st
->prepared
[st
->use_file
])
760 for (j
= 0; commands
[j
] != NULL
; j
++)
763 char name
[MAX_PREPARE_NAME
];
765 if (commands
[j
]->type
!= SQL_COMMAND
)
767 preparedStatementName(name
, st
->use_file
, j
);
768 res
= PQprepare(st
->con
, name
,
769 commands
[j
]->argv
[0], commands
[j
]->argc
- 1, NULL
);
770 if (PQresultStatus(res
) != PGRES_COMMAND_OK
)
771 fprintf(stderr
, "%s", PQerrorMessage(st
->con
));
774 st
->prepared
[st
->use_file
] = true;
777 getQueryParams(st
, command
, params
);
778 preparedStatementName(name
, st
->use_file
, st
->state
);
781 fprintf(stderr
, "client %d sending %s\n", n
, name
);
782 r
= PQsendQueryPrepared(st
->con
, name
, command
->argc
- 1,
783 params
, NULL
, NULL
, 0);
785 else /* unknown sql mode */
791 fprintf(stderr
, "client %d cannot send %s\n", n
, command
->argv
[0]);
795 st
->listen
= 1; /* flags that should be listened */
797 else if (commands
[st
->state
]->type
== META_COMMAND
)
799 int argc
= commands
[st
->state
]->argc
,
801 char **argv
= commands
[st
->state
]->argv
;
805 fprintf(stderr
, "client %d executing \\%s", n
, argv
[0]);
806 for (i
= 1; i
< argc
; i
++)
807 fprintf(stderr
, " %s", argv
[i
]);
808 fprintf(stderr
, "\n");
811 if (pg_strcasecmp(argv
[0], "setrandom") == 0)
820 if ((var
= getVariable(st
, argv
[2] + 1)) == NULL
)
822 fprintf(stderr
, "%s: undefined variable %s\n", argv
[0], argv
[2]);
834 fprintf(stderr
, "%s: invalid minimum number %d\n", argv
[0], min
);
842 if ((var
= getVariable(st
, argv
[3] + 1)) == NULL
)
844 fprintf(stderr
, "%s: undefined variable %s\n", argv
[0], argv
[3]);
853 if (max
< min
|| max
> MAX_RANDOM_VALUE
)
855 fprintf(stderr
, "%s: invalid maximum number %d\n", argv
[0], max
);
861 printf("min: %d max: %d random: %d\n", min
, max
, getrand(min
, max
));
863 snprintf(res
, sizeof(res
), "%d", getrand(min
, max
));
865 if (putVariable(st
, argv
[1], res
) == false)
867 fprintf(stderr
, "%s: out of memory\n", argv
[0]);
874 else if (pg_strcasecmp(argv
[0], "set") == 0)
883 if ((var
= getVariable(st
, argv
[2] + 1)) == NULL
)
885 fprintf(stderr
, "%s: undefined variable %s\n", argv
[0], argv
[2]);
892 ope1
= atoi(argv
[2]);
895 snprintf(res
, sizeof(res
), "%d", ope1
);
900 if ((var
= getVariable(st
, argv
[4] + 1)) == NULL
)
902 fprintf(stderr
, "%s: undefined variable %s\n", argv
[0], argv
[4]);
909 ope2
= atoi(argv
[4]);
911 if (strcmp(argv
[3], "+") == 0)
912 snprintf(res
, sizeof(res
), "%d", ope1
+ ope2
);
913 else if (strcmp(argv
[3], "-") == 0)
914 snprintf(res
, sizeof(res
), "%d", ope1
- ope2
);
915 else if (strcmp(argv
[3], "*") == 0)
916 snprintf(res
, sizeof(res
), "%d", ope1
* ope2
);
917 else if (strcmp(argv
[3], "/") == 0)
921 fprintf(stderr
, "%s: division by zero\n", argv
[0]);
925 snprintf(res
, sizeof(res
), "%d", ope1
/ ope2
);
929 fprintf(stderr
, "%s: unsupported operator %s\n", argv
[0], argv
[3]);
935 if (putVariable(st
, argv
[1], res
) == false)
937 fprintf(stderr
, "%s: out of memory\n", argv
[0]);
944 else if (pg_strcasecmp(argv
[0], "sleep") == 0)
952 if ((var
= getVariable(st
, argv
[1] + 1)) == NULL
)
954 fprintf(stderr
, "%s: undefined variable %s\n", argv
[0], argv
[1]);
961 usec
= atoi(argv
[1]);
965 if (pg_strcasecmp(argv
[2], "ms") == 0)
967 else if (pg_strcasecmp(argv
[2], "s") == 0)
973 gettimeofday(&now
, NULL
);
974 st
->until
.tv_sec
= now
.tv_sec
+ (now
.tv_usec
+ usec
) / 1000000;
975 st
->until
.tv_usec
= (now
.tv_usec
+ usec
) % 1000000;
985 /* discard connections */
987 disconnect_all(CState
* state
)
991 for (i
= 0; i
< nclients
; i
++)
994 PQfinish(state
[i
].con
);
998 /* create tables and setup data */
1003 * Note: TPC-B requires at least 100 bytes per row, and the "filler"
1004 * fields in these table declarations were intended to comply with that.
1005 * But because they default to NULLs, they don't actually take any
1006 * space. We could fix that by giving them non-null default values.
1007 * However, that would completely break comparability of pgbench
1008 * results with prior versions. Since pgbench has never pretended
1009 * to be fully TPC-B compliant anyway, we stick with the historical
1012 static char *DDLs
[] = {
1013 "drop table if exists pgbench_branches",
1014 "create table pgbench_branches(bid int not null,bbalance int,filler char(88)) with (fillfactor=%d)",
1015 "drop table if exists pgbench_tellers",
1016 "create table pgbench_tellers(tid int not null,bid int,tbalance int,filler char(84)) with (fillfactor=%d)",
1017 "drop table if exists pgbench_accounts",
1018 "create table pgbench_accounts(aid int not null,bid int,abalance int,filler char(84)) with (fillfactor=%d)",
1019 "drop table if exists pgbench_history",
1020 "create table pgbench_history(tid int,bid int,aid int,delta int,mtime timestamp,filler char(22))"
1022 static char *DDLAFTERs
[] = {
1023 "alter table pgbench_branches add primary key (bid)",
1024 "alter table pgbench_tellers add primary key (tid)",
1025 "alter table pgbench_accounts add primary key (aid)"
1033 if ((con
= doConnect()) == NULL
)
1036 for (i
= 0; i
< lengthof(DDLs
); i
++)
1039 * set fillfactor for branches, tellers and accounts tables
1041 if ((strstr(DDLs
[i
], "create table pgbench_branches") == DDLs
[i
]) ||
1042 (strstr(DDLs
[i
], "create table pgbench_tellers") == DDLs
[i
]) ||
1043 (strstr(DDLs
[i
], "create table pgbench_accounts") == DDLs
[i
]))
1047 snprintf(ddl_stmt
, 128, DDLs
[i
], fillfactor
);
1048 executeStatement(con
, ddl_stmt
);
1052 executeStatement(con
, DDLs
[i
]);
1055 executeStatement(con
, "begin");
1057 for (i
= 0; i
< nbranches
* scale
; i
++)
1059 snprintf(sql
, 256, "insert into pgbench_branches(bid,bbalance) values(%d,0)", i
+ 1);
1060 executeStatement(con
, sql
);
1063 for (i
= 0; i
< ntellers
* scale
; i
++)
1065 snprintf(sql
, 256, "insert into pgbench_tellers(tid,bid,tbalance) values (%d,%d,0)"
1066 ,i
+ 1, i
/ ntellers
+ 1);
1067 executeStatement(con
, sql
);
1070 executeStatement(con
, "commit");
1073 * fill the pgbench_accounts table with some data
1075 fprintf(stderr
, "creating tables...\n");
1077 executeStatement(con
, "begin");
1078 executeStatement(con
, "truncate pgbench_accounts");
1080 res
= PQexec(con
, "copy pgbench_accounts from stdin");
1081 if (PQresultStatus(res
) != PGRES_COPY_IN
)
1083 fprintf(stderr
, "%s", PQerrorMessage(con
));
1088 for (i
= 0; i
< naccounts
* scale
; i
++)
1092 snprintf(sql
, 256, "%d\t%d\t%d\t\n", j
, i
/ naccounts
+ 1, 0);
1093 if (PQputline(con
, sql
))
1095 fprintf(stderr
, "PQputline failed\n");
1100 fprintf(stderr
, "%d tuples done.\n", j
);
1102 if (PQputline(con
, "\\.\n"))
1104 fprintf(stderr
, "very last PQputline failed\n");
1109 fprintf(stderr
, "PQendcopy failed\n");
1112 executeStatement(con
, "commit");
1117 fprintf(stderr
, "set primary key...\n");
1118 for (i
= 0; i
< lengthof(DDLAFTERs
); i
++)
1119 executeStatement(con
, DDLAFTERs
[i
]);
1122 fprintf(stderr
, "vacuum...");
1123 executeStatement(con
, "vacuum analyze pgbench_branches");
1124 executeStatement(con
, "vacuum analyze pgbench_tellers");
1125 executeStatement(con
, "vacuum analyze pgbench_accounts");
1126 executeStatement(con
, "vacuum analyze pgbench_history");
1128 fprintf(stderr
, "done.\n");
1133 * Parse the raw sql and replace :param to $n.
1136 parseQuery(Command
*cmd
, const char *raw_sql
)
1141 sql
= strdup(raw_sql
);
1147 while ((p
= strchr(p
, ':')) != NULL
)
1153 name
= parseVariable(p
, &eaten
);
1156 while (*p
== ':') { p
++; }
1160 if (cmd
->argc
>= MAX_ARGS
)
1162 fprintf(stderr
, "statement has too many arguments (maximum is %d): %s\n", MAX_ARGS
- 1, raw_sql
);
1166 sprintf(var
, "$%d", cmd
->argc
);
1167 if ((p
= replaceVariable(&sql
, p
, eaten
, var
)) == NULL
)
1170 cmd
->argv
[cmd
->argc
] = name
;
1179 process_commands(char *buf
)
1181 const char delim
[] = " \f\n\r\t\v";
1183 Command
*my_commands
;
1188 if ((p
= strchr(buf
, '\n')) != NULL
)
1192 while (isspace((unsigned char) *p
))
1195 if (*p
== '\0' || strncmp(p
, "--", 2) == 0)
1200 my_commands
= (Command
*) malloc(sizeof(Command
));
1201 if (my_commands
== NULL
)
1206 my_commands
->argc
= 0;
1210 my_commands
->type
= META_COMMAND
;
1213 tok
= strtok(++p
, delim
);
1217 if ((my_commands
->argv
[j
] = strdup(tok
)) == NULL
)
1220 my_commands
->argc
++;
1223 tok
= strtok(NULL
, delim
);
1226 if (pg_strcasecmp(my_commands
->argv
[0], "setrandom") == 0)
1228 if (my_commands
->argc
< 4)
1230 fprintf(stderr
, "%s: missing argument\n", my_commands
->argv
[0]);
1234 for (j
= 4; j
< my_commands
->argc
; j
++)
1235 fprintf(stderr
, "%s: extra argument \"%s\" ignored\n",
1236 my_commands
->argv
[0], my_commands
->argv
[j
]);
1238 else if (pg_strcasecmp(my_commands
->argv
[0], "set") == 0)
1240 if (my_commands
->argc
< 3)
1242 fprintf(stderr
, "%s: missing argument\n", my_commands
->argv
[0]);
1246 for (j
= my_commands
->argc
< 5 ? 3 : 5; j
< my_commands
->argc
; j
++)
1247 fprintf(stderr
, "%s: extra argument \"%s\" ignored\n",
1248 my_commands
->argv
[0], my_commands
->argv
[j
]);
1250 else if (pg_strcasecmp(my_commands
->argv
[0], "sleep") == 0)
1252 if (my_commands
->argc
< 2)
1254 fprintf(stderr
, "%s: missing argument\n", my_commands
->argv
[0]);
1258 if (my_commands
->argc
>= 3)
1260 if (pg_strcasecmp(my_commands
->argv
[2], "us") != 0 &&
1261 pg_strcasecmp(my_commands
->argv
[2], "ms") != 0 &&
1262 pg_strcasecmp(my_commands
->argv
[2], "s"))
1264 fprintf(stderr
, "%s: unknown time unit '%s' - must be us, ms or s\n",
1265 my_commands
->argv
[0], my_commands
->argv
[2]);
1270 for (j
= 3; j
< my_commands
->argc
; j
++)
1271 fprintf(stderr
, "%s: extra argument \"%s\" ignored\n",
1272 my_commands
->argv
[0], my_commands
->argv
[j
]);
1276 fprintf(stderr
, "Invalid command %s\n", my_commands
->argv
[0]);
1282 my_commands
->type
= SQL_COMMAND
;
1287 if ((my_commands
->argv
[0] = strdup(p
)) == NULL
)
1289 my_commands
->argc
++;
1291 case QUERY_EXTENDED
:
1292 case QUERY_PREPARED
:
1293 if (!parseQuery(my_commands
, p
))
1305 process_file(char *filename
)
1307 #define COMMANDS_ALLOC_NUM 128
1309 Command
**my_commands
;
1315 if (num_files
>= MAX_FILES
)
1317 fprintf(stderr
, "Up to only %d SQL files are allowed\n", MAX_FILES
);
1321 alloc_num
= COMMANDS_ALLOC_NUM
;
1322 my_commands
= (Command
**) malloc(sizeof(Command
*) * alloc_num
);
1323 if (my_commands
== NULL
)
1326 if (strcmp(filename
, "-") == 0)
1328 else if ((fd
= fopen(filename
, "r")) == NULL
)
1330 fprintf(stderr
, "%s: %s\n", filename
, strerror(errno
));
1336 while (fgets(buf
, sizeof(buf
), fd
) != NULL
)
1342 while (isspace((unsigned char) buf
[i
]))
1345 if (buf
[i
] != '\0' && strncmp(&buf
[i
], "--", 2) != 0)
1347 commands
= process_commands(&buf
[i
]);
1348 if (commands
== NULL
)
1357 my_commands
[lineno
] = commands
;
1360 if (lineno
>= alloc_num
)
1362 alloc_num
+= COMMANDS_ALLOC_NUM
;
1363 my_commands
= realloc(my_commands
, sizeof(Command
*) * alloc_num
);
1364 if (my_commands
== NULL
)
1373 my_commands
[lineno
] = NULL
;
1375 sql_files
[num_files
++] = my_commands
;
1381 process_builtin(char *tb
)
1383 #define COMMANDS_ALLOC_NUM 128
1385 Command
**my_commands
;
1393 alloc_num
= COMMANDS_ALLOC_NUM
;
1394 my_commands
= (Command
**) malloc(sizeof(Command
*) * alloc_num
);
1395 if (my_commands
== NULL
)
1406 while (*tb
&& *tb
!= '\n')
1417 commands
= process_commands(buf
);
1418 if (commands
== NULL
)
1423 my_commands
[lineno
] = commands
;
1426 if (lineno
>= alloc_num
)
1428 alloc_num
+= COMMANDS_ALLOC_NUM
;
1429 my_commands
= realloc(my_commands
, sizeof(Command
*) * alloc_num
);
1430 if (my_commands
== NULL
)
1437 my_commands
[lineno
] = NULL
;
1442 /* print out results */
1445 int ttype
, CState
* state
,
1446 struct timeval
* start_time
, struct timeval
* end_time
)
1451 int normal_xacts
= 0;
1454 for (i
= 0; i
< nclients
; i
++)
1455 normal_xacts
+= state
[i
].cnt
;
1457 t1
= (end_time
->tv_sec
- start_time
->tv_sec
) * 1000000.0 + (end_time
->tv_usec
- start_time
->tv_usec
);
1458 t1
= normal_xacts
* 1000000.0 / t1
;
1460 t2
= (end_time
->tv_sec
- start_time
->tv_sec
- conn_total_time
.tv_sec
) * 1000000.0 +
1461 (end_time
->tv_usec
- start_time
->tv_usec
- conn_total_time
.tv_usec
);
1462 t2
= normal_xacts
* 1000000.0 / t2
;
1465 s
= "TPC-B (sort of)";
1466 else if (ttype
== 2)
1467 s
= "Update only pgbench_accounts";
1468 else if (ttype
== 1)
1473 printf("transaction type: %s\n", s
);
1474 printf("scaling factor: %d\n", scale
);
1475 printf("query mode: %s\n", QUERYMODE
[querymode
]);
1476 printf("number of clients: %d\n", nclients
);
1479 printf("number of transactions per client: %d\n", nxacts
);
1480 printf("number of transactions actually processed: %d/%d\n",
1481 normal_xacts
, nxacts
* nclients
);
1485 printf("duration: %d s\n", duration
);
1486 printf("number of transactions actually processed: %d\n",
1489 printf("tps = %f (including connections establishing)\n", t1
);
1490 printf("tps = %f (excluding connections establishing)\n", t2
);
1495 main(int argc
, char **argv
)
1498 int is_init_mode
= 0; /* initialize mode? */
1499 int is_no_vacuum
= 0; /* no vacuum at all before testing? */
1500 int do_vacuum_accounts
= 0; /* do vacuum accounts before testing? */
1501 int debug
= 0; /* debug flag */
1502 int ttype
= 0; /* transaction type. 0: TPC-B, 1: SELECT only,
1503 * 2: skip update of branches and tellers */
1504 char *filename
= NULL
;
1505 bool scale_given
= false;
1507 CState
*state
; /* status of clients */
1509 struct timeval start_time
; /* start up time */
1510 struct timeval end_time
; /* end time */
1515 int nsocks
; /* return from select(2) */
1516 int maxsock
; /* max socket number to be waited */
1518 struct timeval timeout
;
1521 #ifdef HAVE_GETRLIMIT
1531 const char *progname
;
1533 progname
= get_progname(argv
[0]);
1537 if (strcmp(argv
[1], "--help") == 0 || strcmp(argv
[1], "-?") == 0)
1542 if (strcmp(argv
[1], "--version") == 0 || strcmp(argv
[1], "-V") == 0)
1544 puts("pgbench (PostgreSQL) " PG_VERSION
);
1550 /* stderr is buffered on Win32. */
1551 setvbuf(stderr
, NULL
, _IONBF
, 0);
1554 if ((env
= getenv("PGHOST")) != NULL
&& *env
!= '\0')
1556 if ((env
= getenv("PGPORT")) != NULL
&& *env
!= '\0')
1558 else if ((env
= getenv("PGUSER")) != NULL
&& *env
!= '\0')
1561 state
= (CState
*) malloc(sizeof(CState
));
1564 fprintf(stderr
, "Couldn't allocate memory for state\n");
1568 memset(state
, 0, sizeof(*state
));
1570 while ((c
= getopt(argc
, argv
, "ih:nvp:dSNc:Cs:t:T:U:lf:D:F:M:")) != -1)
1584 do_vacuum_accounts
++;
1599 nclients
= atoi(optarg
);
1600 if (nclients
<= 0 || nclients
> MAXCLIENTS
)
1602 fprintf(stderr
, "invalid number of clients: %d\n", nclients
);
1605 #ifdef HAVE_GETRLIMIT
1606 #ifdef RLIMIT_NOFILE /* most platforms use RLIMIT_NOFILE */
1607 if (getrlimit(RLIMIT_NOFILE
, &rlim
) == -1)
1608 #else /* but BSD doesn't ... */
1609 if (getrlimit(RLIMIT_OFILE
, &rlim
) == -1)
1610 #endif /* RLIMIT_NOFILE */
1612 fprintf(stderr
, "getrlimit failed: %s\n", strerror(errno
));
1615 if (rlim
.rlim_cur
<= (nclients
+ 2))
1617 fprintf(stderr
, "You need at least %d open files but you are only allowed to use %ld.\n", nclients
+ 2, (long) rlim
.rlim_cur
);
1618 fprintf(stderr
, "Use limit/ulimit to increase the limit before using pgbench.\n");
1621 #endif /* HAVE_GETRLIMIT */
1628 scale
= atoi(optarg
);
1631 fprintf(stderr
, "invalid scaling factor: %d\n", scale
);
1638 fprintf(stderr
, "specify either a number of transactions (-t) or a duration (-T), not both.\n");
1641 nxacts
= atoi(optarg
);
1644 fprintf(stderr
, "invalid number of transactions: %d\n", nxacts
);
1651 fprintf(stderr
, "specify either a number of transactions (-t) or a duration (-T), not both.\n");
1654 duration
= atoi(optarg
);
1657 fprintf(stderr
, "invalid duration: %d\n", duration
);
1670 if (process_file(filename
) == false || *sql_files
[num_files
- 1] == NULL
)
1677 if ((p
= strchr(optarg
, '=')) == NULL
|| p
== optarg
|| *(p
+ 1) == '\0')
1679 fprintf(stderr
, "invalid variable definition: %s\n", optarg
);
1684 if (putVariable(&state
[0], optarg
, p
) == false)
1686 fprintf(stderr
, "Couldn't allocate memory for variable\n");
1692 fillfactor
= atoi(optarg
);
1693 if ((fillfactor
< 10) || (fillfactor
> 100))
1695 fprintf(stderr
, "invalid fillfactor: %d\n", fillfactor
);
1702 fprintf(stderr
, "query mode (-M) should be specifiled before transaction scripts (-f)\n");
1705 for (querymode
= 0; querymode
< NUM_QUERYMODE
; querymode
++)
1706 if (strcmp(optarg
, QUERYMODE
[querymode
]) == 0)
1708 if (querymode
>= NUM_QUERYMODE
)
1710 fprintf(stderr
, "invalid query mode (-M): %s\n", optarg
);
1715 fprintf(stderr
, _("Try \"%s --help\" for more information.\n"), progname
);
1722 dbName
= argv
[optind
];
1725 if ((env
= getenv("PGDATABASE")) != NULL
&& *env
!= '\0')
1727 else if (login
!= NULL
&& *login
!= '\0')
1739 /* Use DEFAULT_NXACTS if neither nxacts nor duration is specified. */
1740 if (nxacts
<= 0 && duration
<= 0)
1741 nxacts
= DEFAULT_NXACTS
;
1747 state
= (CState
*) realloc(state
, sizeof(CState
) * nclients
);
1750 fprintf(stderr
, "Couldn't allocate memory for state\n");
1754 memset(state
+ 1, 0, sizeof(*state
) * (nclients
- 1));
1756 /* copy any -D switch values to all clients */
1757 for (i
= 1; i
< nclients
; i
++)
1761 for (j
= 0; j
< state
[0].nvariables
; j
++)
1763 if (putVariable(&state
[i
], state
[0].variables
[j
].name
, state
[0].variables
[j
].value
) == false)
1765 fprintf(stderr
, "Couldn't allocate memory for variable\n");
1776 snprintf(logpath
, 64, "pgbench_log.%d", (int) getpid());
1777 LOGFILE
= fopen(logpath
, "w");
1779 if (LOGFILE
== NULL
)
1781 fprintf(stderr
, "Couldn't open logfile \"%s\": %s", logpath
, strerror(errno
));
1789 printf("pghost: %s pgport: %s nclients: %d nxacts: %d dbName: %s\n",
1790 pghost
, pgport
, nclients
, nxacts
, dbName
);
1792 printf("pghost: %s pgport: %s nclients: %d duration: %d dbName: %s\n",
1793 pghost
, pgport
, nclients
, duration
, dbName
);
1796 /* opening connection... */
1801 if (PQstatus(con
) == CONNECTION_BAD
)
1803 fprintf(stderr
, "Connection to database '%s' failed.\n", dbName
);
1804 fprintf(stderr
, "%s", PQerrorMessage(con
));
1811 * get the scaling factor that should be same as count(*) from
1812 * pgbench_branches if this is not a custom query
1814 res
= PQexec(con
, "select count(*) from pgbench_branches");
1815 if (PQresultStatus(res
) != PGRES_TUPLES_OK
)
1817 fprintf(stderr
, "%s", PQerrorMessage(con
));
1820 scale
= atoi(PQgetvalue(res
, 0, 0));
1823 fprintf(stderr
, "count(*) from pgbench_branches invalid (%d)\n", scale
);
1828 /* warn if we override user-given -s switch */
1831 "Scale option ignored, using pgbench_branches table count = %d\n",
1836 * :scale variables normally get -s or database scale, but don't override
1837 * an explicit -D switch
1839 if (getVariable(&state
[0], "scale") == NULL
)
1841 snprintf(val
, sizeof(val
), "%d", scale
);
1842 for (i
= 0; i
< nclients
; i
++)
1844 if (putVariable(&state
[i
], "scale", val
) == false)
1846 fprintf(stderr
, "Couldn't allocate memory for variable\n");
1854 fprintf(stderr
, "starting vacuum...");
1855 executeStatement(con
, "vacuum pgbench_branches");
1856 executeStatement(con
, "vacuum pgbench_tellers");
1857 executeStatement(con
, "truncate pgbench_history");
1858 fprintf(stderr
, "end.\n");
1860 if (do_vacuum_accounts
)
1862 fprintf(stderr
, "starting vacuum pgbench_accounts...");
1863 executeStatement(con
, "vacuum analyze pgbench_accounts");
1864 fprintf(stderr
, "end.\n");
1869 /* set random seed */
1870 gettimeofday(&start_time
, NULL
);
1871 srandom((unsigned int) start_time
.tv_usec
);
1873 /* get start up time */
1874 gettimeofday(&start_time
, NULL
);
1876 /* set alarm if duration is specified. */
1880 if (is_connect
== 0)
1882 struct timeval t
, now
;
1884 /* make connections to the database */
1885 for (i
= 0; i
< nclients
; i
++)
1888 if ((state
[i
].con
= doConnect()) == NULL
)
1891 /* time after connections set up */
1892 gettimeofday(&now
, NULL
);
1893 diffTime(&now
, &start_time
, &t
);
1894 addTime(&conn_total_time
, &t
, &conn_total_time
);
1897 /* process bultin SQL scripts */
1901 sql_files
[0] = process_builtin(tpc_b
);
1906 sql_files
[0] = process_builtin(select_only
);
1911 sql_files
[0] = process_builtin(simple_update
);
1919 /* send start up queries in async manner */
1920 for (i
= 0; i
< nclients
; i
++)
1922 Command
**commands
= sql_files
[state
[i
].use_file
];
1923 int prev_ecnt
= state
[i
].ecnt
;
1925 state
[i
].use_file
= getrand(0, num_files
- 1);
1926 doCustom(state
, i
, debug
);
1928 if (state
[i
].ecnt
> prev_ecnt
&& commands
[state
[i
].state
]->type
== META_COMMAND
)
1930 fprintf(stderr
, "Client %d aborted in state %d. Execution meta-command failed.\n", i
, state
[i
].state
);
1931 remains
--; /* I've aborted */
1932 PQfinish(state
[i
].con
);
1933 state
[i
].con
= NULL
;
1941 disconnect_all(state
);
1943 gettimeofday(&end_time
, NULL
);
1944 printResults(ttype
, state
, &start_time
, &end_time
);
1950 FD_ZERO(&input_mask
);
1954 for (i
= 0; i
< nclients
; i
++)
1956 Command
**commands
= sql_files
[state
[i
].use_file
];
1958 if (state
[i
].sleeping
)
1961 int sock
= PQsocket(state
[i
].con
);
1965 gettimeofday(&now
, NULL
);
1969 this_usec
= (state
[i
].until
.tv_sec
- now
.tv_sec
) * 1000000 +
1970 state
[i
].until
.tv_usec
- now
.tv_usec
;
1972 if (this_usec
> 0 && (min_usec
== 0 || this_usec
< min_usec
))
1973 min_usec
= this_usec
;
1975 FD_SET(sock
, &input_mask
);
1979 else if (state
[i
].con
&& commands
[state
[i
].state
]->type
!= META_COMMAND
)
1981 int sock
= PQsocket(state
[i
].con
);
1985 disconnect_all(state
);
1988 FD_SET(sock
, &input_mask
);
1998 timeout
.tv_sec
= min_usec
/ 1000000;
1999 timeout
.tv_usec
= min_usec
% 1000000;
2001 nsocks
= select(maxsock
+ 1, &input_mask
, (fd_set
*) NULL
,
2002 (fd_set
*) NULL
, &timeout
);
2005 nsocks
= select(maxsock
+ 1, &input_mask
, (fd_set
*) NULL
,
2006 (fd_set
*) NULL
, (struct timeval
*) NULL
);
2011 /* must be something wrong */
2012 disconnect_all(state
);
2013 fprintf(stderr
, "select failed: %s\n", strerror(errno
));
2017 else if (nsocks
== 0)
2019 fprintf(stderr
, "select timeout\n");
2020 for (i
= 0; i
< nclients
; i
++)
2022 fprintf(stderr
, "client %d:state %d cnt %d ecnt %d listen %d\n",
2023 i
, state
[i
].state
, state
[i
].cnt
, state
[i
].ecnt
, state
[i
].listen
);
2030 /* ok, backend returns reply */
2031 for (i
= 0; i
< nclients
; i
++)
2033 Command
**commands
= sql_files
[state
[i
].use_file
];
2034 int prev_ecnt
= state
[i
].ecnt
;
2036 if (state
[i
].con
&& (FD_ISSET(PQsocket(state
[i
].con
), &input_mask
)
2037 || commands
[state
[i
].state
]->type
== META_COMMAND
))
2039 doCustom(state
, i
, debug
);
2042 if (state
[i
].ecnt
> prev_ecnt
&& commands
[state
[i
].state
]->type
== META_COMMAND
)
2044 fprintf(stderr
, "Client %d aborted in state %d. Execution of meta-command failed.\n", i
, state
[i
].state
);
2045 remains
--; /* I've aborted */
2046 PQfinish(state
[i
].con
);
2047 state
[i
].con
= NULL
;
2055 * Support for duration option: set timer_exceeded after so many seconds.
2061 handle_sig_alarm(SIGNAL_ARGS
)
2063 timer_exceeded
= true;
2067 setalarm(int seconds
)
2069 pqsignal(SIGALRM
, handle_sig_alarm
);
2075 static VOID CALLBACK
2076 win32_timer_callback(PVOID lpParameter
, BOOLEAN TimerOrWaitFired
)
2078 timer_exceeded
= true;
2082 setalarm(int seconds
)
2087 /* This function will be called at most once, so we can cheat a bit. */
2088 queue
= CreateTimerQueue();
2089 if (seconds
> ((DWORD
)-1) / 1000 ||
2090 !CreateTimerQueueTimer(&timer
, queue
,
2091 win32_timer_callback
, NULL
, seconds
* 1000, 0,
2092 WT_EXECUTEINTIMERTHREAD
| WT_EXECUTEONLYONCE
))
2094 fprintf(stderr
, "Failed to set timer\n");