4 * A simple benchmark program for PostgreSQL
5 * Originally written by Tatsuo Ishii and enhanced by many contributors.
7 * $PostgreSQL: pgsql/contrib/pgbench/pgbench.c,v 1.83 2009/01/01 17:23:32 momjian Exp $
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 of
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 accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
185 "SELECT abalance FROM accounts WHERE aid = :aid;\n"
186 "UPDATE tellers SET tbalance = tbalance + :delta WHERE tid = :tid;\n"
187 "UPDATE branches SET bbalance = bbalance + :delta WHERE bid = :bid;\n"
188 "INSERT INTO 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 accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
203 "SELECT abalance FROM accounts WHERE aid = :aid;\n"
204 "INSERT INTO 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 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;
255 fprintf(stderr, "usage: pgbench [-h hostname][-p port][-c nclients][-t ntransactions | -T duration][-s scaling_factor][-D varname=value][-n][-C][-v][-S][-N][-M querymode][-f filename][-l][-U login][-d][dbname]\n");
256 fprintf(stderr, "(initialize mode): pgbench -i [-h hostname][-p port][-s scaling_factor] [-F fillfactor] [-U login][-d][dbname]\n");
259 /* random number generator: uniform distribution from min to max inclusive */
261 getrand(int min, int max)
264 * Odd coding is so that min and max have approximately the same chance of
265 * being selected as do numbers between them.
267 return min + (int) (((max - min + 1) * (double) random()) / (MAX_RANDOM_VALUE + 1.0));
270 /* call PQexec() and exit() on failure */
272 executeStatement(PGconn *con, const char *sql)
276 res = PQexec(con, sql);
277 if (PQresultStatus(res) != PGRES_COMMAND_OK)
279 fprintf(stderr, "%s", PQerrorMessage(con));
285 /* set up a connection to the backend */
290 static char *password = NULL;
294 * Start the connection. Loop until we have a password if requested by
301 conn = PQsetdbLogin(pghost, pgport, pgoptions, pgtty, dbName,
305 fprintf(stderr, "Connection to database \"%s\" failed\n",
310 if (PQstatus(conn) == CONNECTION_BAD &&
311 PQconnectionNeedsPassword(conn) &&
316 password = simple_prompt("Password: ", 100, false);
321 /* check to see that the backend connection was successfully made */
322 if (PQstatus(conn) == CONNECTION_BAD)
324 fprintf(stderr, "Connection to database \"%s\" failed:\n%s",
325 dbName, PQerrorMessage(conn));
330 executeStatement(conn, "SET search_path = public");
335 /* throw away response from backend */
337 discard_response(CState * state)
343 res = PQgetResult(state->con);
349 /* check to see if the SQL result was good */
351 check(CState * state, PGresult *res, int n)
353 CState *st = &state[n];
355 switch (PQresultStatus(res))
357 case PGRES_COMMAND_OK:
358 case PGRES_TUPLES_OK:
362 fprintf(stderr, "Client %d aborted in state %d: %s",
363 n, st->state, PQerrorMessage(st->con));
364 remains--; /* I've aborted */
373 compareVariables(const void *v1, const void *v2)
375 return strcmp(((const Variable *) v1)->name,
376 ((const Variable *) v2)->name);
380 getVariable(CState * st, char *name)
385 /* On some versions of Solaris, bsearch of zero items dumps core */
386 if (st->nvariables <= 0)
390 var = (Variable *) bsearch((void *) &key,
391 (void *) st->variables,
402 putVariable(CState * st, char *name, char *value)
408 /* On some versions of Solaris, bsearch of zero items dumps core */
409 if (st->nvariables > 0)
410 var = (Variable *) bsearch((void *) &key,
411 (void *) st->variables,
423 newvars = (Variable *) realloc(st->variables,
424 (st->nvariables + 1) * sizeof(Variable));
426 newvars = (Variable *) malloc(sizeof(Variable));
431 st->variables = newvars;
433 var = &newvars[st->nvariables];
438 if ((var->name = strdup(name)) == NULL
439 || (var->value = strdup(value)) == NULL)
448 qsort((void *) st->variables, st->nvariables, sizeof(Variable),
455 if ((val = strdup(value)) == NULL)
466 parseVariable(const char *sql, int *eaten)
474 } while (isalnum((unsigned char) sql[i]) || sql[i] == '_');
481 memcpy(name, &sql[1], i - 1);
489 replaceVariable(char **sql, char *param, int len, char *value)
491 int valueln = strlen(value);
496 size_t offset = param - *sql;
498 tmp = realloc(*sql, strlen(*sql) - len + valueln + 1);
505 param = *sql + offset;
509 memmove(param + valueln, param + len, strlen(param + len) + 1);
510 strncpy(param, value, valueln);
512 return param + valueln;
516 assignVariables(CState * st, char *sql)
523 while ((p = strchr(p, ':')) != NULL)
527 name = parseVariable(p, &eaten);
530 while (*p == ':') { p++; }
534 val = getVariable(st, name);
542 if ((p = replaceVariable(&sql, p, eaten, val)) == NULL)
550 getQueryParams(CState *st, const Command *command, const char **params)
554 for (i = 0; i < command->argc - 1; i++)
555 params[i] = getVariable(st, command->argv[i+1]);
558 #define MAX_PREPARE_NAME 32
560 preparedStatementName(char *buffer, int file, int state)
562 sprintf(buffer, "P%d_%d", file, state);
566 doCustom(CState * state, int n, int debug)
569 CState *st = &state[n];
573 commands = sql_files[st->use_file];
576 { /* are we sleeping? */
580 gettimeofday(&now, NULL);
581 usec = (st->until.tv_sec - now.tv_sec) * 1000000 +
582 st->until.tv_usec - now.tv_usec;
584 st->sleeping = 0; /* Done sleeping, go ahead with next command */
586 return; /* Still sleeping, nothing to do here */
590 { /* are we receiver? */
591 if (commands[st->state]->type == SQL_COMMAND)
594 fprintf(stderr, "client %d receiving\n", n);
595 if (!PQconsumeInput(st->con))
596 { /* there's something wrong */
597 fprintf(stderr, "Client %d aborted in state %d. Probably the backend died while processing.\n", n, st->state);
598 remains--; /* I've aborted */
603 if (PQisBusy(st->con))
604 return; /* don't have the whole result yet */
608 * transaction finished: record the time it took in the log
610 if (use_log && commands[st->state + 1] == NULL)
615 gettimeofday(&now, NULL);
616 diff = (int) (now.tv_sec - st->txn_begin.tv_sec) * 1000000.0 +
617 (int) (now.tv_usec - st->txn_begin.tv_usec);
619 fprintf(LOGFILE, "%d %d %.0f %d %ld %ld\n",
620 st->id, st->cnt, diff, st->use_file,
621 (long) now.tv_sec, (long) now.tv_usec);
624 if (commands[st->state]->type == SQL_COMMAND)
626 res = PQgetResult(st->con);
627 if (check(state, res, n))
633 discard_response(st);
636 if (commands[st->state + 1] == NULL)
645 if ((st->cnt >= nxacts && duration <= 0) || timer_exceeded)
647 remains--; /* I've done */
657 /* increment state counter */
659 if (commands[st->state] == NULL)
662 st->use_file = getrand(0, num_files - 1);
663 commands = sql_files[st->use_file];
669 struct timeval t1, t2, t3;
671 gettimeofday(&t1, NULL);
672 if ((st->con = doConnect()) == NULL)
674 fprintf(stderr, "Client %d aborted in establishing connection.\n",
676 remains--; /* I've aborted */
681 gettimeofday(&t2, NULL);
682 diffTime(&t2, &t1, &t3);
683 addTime(&conn_total_time, &t3, &conn_total_time);
686 if (use_log && st->state == 0)
687 gettimeofday(&(st->txn_begin), NULL);
689 if (commands[st->state]->type == SQL_COMMAND)
691 const Command *command = commands[st->state];
694 if (querymode == QUERY_SIMPLE)
698 if ((sql = strdup(command->argv[0])) == NULL
699 || (sql = assignVariables(st, sql)) == NULL)
701 fprintf(stderr, "out of memory\n");
707 fprintf(stderr, "client %d sending %s\n", n, sql);
708 r = PQsendQuery(st->con, sql);
711 else if (querymode == QUERY_EXTENDED)
713 const char *sql = command->argv[0];
714 const char *params[MAX_ARGS];
716 getQueryParams(st, command, params);
719 fprintf(stderr, "client %d sending %s\n", n, sql);
720 r = PQsendQueryParams(st->con, sql, command->argc - 1,
721 NULL, params, NULL, NULL, 0);
723 else if (querymode == QUERY_PREPARED)
725 char name[MAX_PREPARE_NAME];
726 const char *params[MAX_ARGS];
728 if (!st->prepared[st->use_file])
732 for (j = 0; commands[j] != NULL; j++)
735 char name[MAX_PREPARE_NAME];
737 if (commands[j]->type != SQL_COMMAND)
739 preparedStatementName(name, st->use_file, j);
740 res = PQprepare(st->con, name,
741 commands[j]->argv[0], commands[j]->argc - 1, NULL);
742 if (PQresultStatus(res) != PGRES_COMMAND_OK)
743 fprintf(stderr, "%s", PQerrorMessage(st->con));
746 st->prepared[st->use_file] = true;
749 getQueryParams(st, command, params);
750 preparedStatementName(name, st->use_file, st->state);
753 fprintf(stderr, "client %d sending %s\n", n, name);
754 r = PQsendQueryPrepared(st->con, name, command->argc - 1,
755 params, NULL, NULL, 0);
757 else /* unknown sql mode */
763 fprintf(stderr, "client %d cannot send %s\n", n, command->argv[0]);
767 st->listen = 1; /* flags that should be listened */
769 else if (commands[st->state]->type == META_COMMAND)
771 int argc = commands[st->state]->argc,
773 char **argv = commands[st->state]->argv;
777 fprintf(stderr, "client %d executing \\%s", n, argv[0]);
778 for (i = 1; i < argc; i++)
779 fprintf(stderr, " %s", argv[i]);
780 fprintf(stderr, "\n");
783 if (pg_strcasecmp(argv[0], "setrandom") == 0)
792 if ((var = getVariable(st, argv[2] + 1)) == NULL)
794 fprintf(stderr, "%s: undefined variable %s\n", argv[0], argv[2]);
806 fprintf(stderr, "%s: invalid minimum number %d\n", argv[0], min);
814 if ((var = getVariable(st, argv[3] + 1)) == NULL)
816 fprintf(stderr, "%s: undefined variable %s\n", argv[0], argv[3]);
825 if (max < min || max > MAX_RANDOM_VALUE)
827 fprintf(stderr, "%s: invalid maximum number %d\n", argv[0], max);
833 printf("min: %d max: %d random: %d\n", min, max, getrand(min, max));
835 snprintf(res, sizeof(res), "%d", getrand(min, max));
837 if (putVariable(st, argv[1], res) == false)
839 fprintf(stderr, "%s: out of memory\n", argv[0]);
846 else if (pg_strcasecmp(argv[0], "set") == 0)
855 if ((var = getVariable(st, argv[2] + 1)) == NULL)
857 fprintf(stderr, "%s: undefined variable %s\n", argv[0], argv[2]);
864 ope1 = atoi(argv[2]);
867 snprintf(res, sizeof(res), "%d", ope1);
872 if ((var = getVariable(st, argv[4] + 1)) == NULL)
874 fprintf(stderr, "%s: undefined variable %s\n", argv[0], argv[4]);
881 ope2 = atoi(argv[4]);
883 if (strcmp(argv[3], "+") == 0)
884 snprintf(res, sizeof(res), "%d", ope1 + ope2);
885 else if (strcmp(argv[3], "-") == 0)
886 snprintf(res, sizeof(res), "%d", ope1 - ope2);
887 else if (strcmp(argv[3], "*") == 0)
888 snprintf(res, sizeof(res), "%d", ope1 * ope2);
889 else if (strcmp(argv[3], "/") == 0)
893 fprintf(stderr, "%s: division by zero\n", argv[0]);
897 snprintf(res, sizeof(res), "%d", ope1 / ope2);
901 fprintf(stderr, "%s: unsupported operator %s\n", argv[0], argv[3]);
907 if (putVariable(st, argv[1], res) == false)
909 fprintf(stderr, "%s: out of memory\n", argv[0]);
916 else if (pg_strcasecmp(argv[0], "sleep") == 0)
924 if ((var = getVariable(st, argv[1] + 1)) == NULL)
926 fprintf(stderr, "%s: undefined variable %s\n", argv[0], argv[1]);
933 usec = atoi(argv[1]);
937 if (pg_strcasecmp(argv[2], "ms") == 0)
939 else if (pg_strcasecmp(argv[2], "s") == 0)
945 gettimeofday(&now, NULL);
946 st->until.tv_sec = now.tv_sec + (now.tv_usec + usec) / 1000000;
947 st->until.tv_usec = (now.tv_usec + usec) % 1000000;
957 /* discard connections */
959 disconnect_all(CState * state)
963 for (i = 0; i < nclients; i++)
966 PQfinish(state[i].con);
970 /* create tables and setup data */
977 * Note: TPC-B requires at least 100 bytes per row, and the "filler"
978 * fields in these table declarations were intended to comply with that.
979 * But because they default to NULLs, they don't actually take any
980 * space. We could fix that by giving them non-null default values.
981 * However, that would completely break comparability of pgbench
982 * results with prior versions. Since pgbench has never pretended
983 * to be fully TPC-B compliant anyway, we stick with the historical
986 static char *DDLs[] = {
987 "drop table if exists branches",
988 "create table branches(bid int not null,bbalance int,filler char(88)) with (fillfactor=%d)",
989 "drop table if exists tellers",
990 "create table tellers(tid int not null,bid int,tbalance int,filler char(84)) with (fillfactor=%d)",
991 "drop table if exists accounts",
992 "create table accounts(aid int not null,bid int,abalance int,filler char(84)) with (fillfactor=%d)",
993 "drop table if exists history",
994 "create table history(tid int,bid int,aid int,delta int,mtime timestamp,filler char(22))"};
995 static char *DDLAFTERs[] = {
996 "alter table branches add primary key (bid)",
997 "alter table tellers add primary key (tid)",
998 "alter table accounts add primary key (aid)"};
1005 if ((con = doConnect()) == NULL)
1008 for (i = 0; i < lengthof(DDLs); i++)
1011 * set fillfactor for branches, tellers and accounts tables
1013 if ((strstr(DDLs[i], "create table branches") == DDLs[i]) ||
1014 (strstr(DDLs[i], "create table tellers") == DDLs[i]) ||
1015 (strstr(DDLs[i], "create table accounts") == DDLs[i]))
1019 snprintf(ddl_stmt, 128, DDLs[i], fillfactor);
1020 executeStatement(con, ddl_stmt);
1024 executeStatement(con, DDLs[i]);
1027 executeStatement(con, "begin");
1029 for (i = 0; i < nbranches * scale; i++)
1031 snprintf(sql, 256, "insert into branches(bid,bbalance) values(%d,0)", i + 1);
1032 executeStatement(con, sql);
1035 for (i = 0; i < ntellers * scale; i++)
1037 snprintf(sql, 256, "insert into tellers(tid,bid,tbalance) values (%d,%d,0)"
1038 ,i + 1, i / ntellers + 1);
1039 executeStatement(con, sql);
1042 executeStatement(con, "commit");
1045 * fill the accounts table with some data
1047 fprintf(stderr, "creating tables...\n");
1049 executeStatement(con, "begin");
1050 executeStatement(con, "truncate accounts");
1052 res = PQexec(con, "copy accounts from stdin");
1053 if (PQresultStatus(res) != PGRES_COPY_IN)
1055 fprintf(stderr, "%s", PQerrorMessage(con));
1060 for (i = 0; i < naccounts * scale; i++)
1064 snprintf(sql, 256, "%d\t%d\t%d\t\n", j, i / naccounts + 1, 0);
1065 if (PQputline(con, sql))
1067 fprintf(stderr, "PQputline failed\n");
1072 fprintf(stderr, "%d tuples done.\n", j);
1074 if (PQputline(con, "\\.\n"))
1076 fprintf(stderr, "very last PQputline failed\n");
1081 fprintf(stderr, "PQendcopy failed\n");
1084 executeStatement(con, "commit");
1089 fprintf(stderr, "set primary key...\n");
1090 for (i = 0; i < lengthof(DDLAFTERs); i++)
1091 executeStatement(con, DDLAFTERs[i]);
1094 fprintf(stderr, "vacuum...");
1095 executeStatement(con, "vacuum analyze branches");
1096 executeStatement(con, "vacuum analyze tellers");
1097 executeStatement(con, "vacuum analyze accounts");
1098 executeStatement(con, "vacuum analyze history");
1100 fprintf(stderr, "done.\n");
1105 * Parse the raw sql and replace :param to $n.
1108 parseQuery(Command *cmd, const char *raw_sql)
1113 sql = strdup(raw_sql);
1119 while ((p = strchr(p, ':')) != NULL)
1125 name = parseVariable(p, &eaten);
1128 while (*p == ':') { p++; }
1132 if (cmd->argc >= MAX_ARGS)
1134 fprintf(stderr, "statement has too many arguments (maximum is %d): %s\n", MAX_ARGS - 1, raw_sql);
1138 sprintf(var, "$%d", cmd->argc);
1139 if ((p = replaceVariable(&sql, p, eaten, var)) == NULL)
1142 cmd->argv[cmd->argc] = name;
1151 process_commands(char *buf)
1153 const char delim[] = " \f\n\r\t\v";
1155 Command *my_commands;
1160 if ((p = strchr(buf, '\n')) != NULL)
1164 while (isspace((unsigned char) *p))
1167 if (*p == '\0' || strncmp(p, "--", 2) == 0)
1172 my_commands = (Command *) malloc(sizeof(Command));
1173 if (my_commands == NULL)
1178 my_commands->argc = 0;
1182 my_commands->type = META_COMMAND;
1185 tok = strtok(++p, delim);
1189 if ((my_commands->argv[j] = strdup(tok)) == NULL)
1192 my_commands->argc++;
1195 tok = strtok(NULL, delim);
1198 if (pg_strcasecmp(my_commands->argv[0], "setrandom") == 0)
1200 if (my_commands->argc < 4)
1202 fprintf(stderr, "%s: missing argument\n", my_commands->argv[0]);
1206 for (j = 4; j < my_commands->argc; j++)
1207 fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
1208 my_commands->argv[0], my_commands->argv[j]);
1210 else if (pg_strcasecmp(my_commands->argv[0], "set") == 0)
1212 if (my_commands->argc < 3)
1214 fprintf(stderr, "%s: missing argument\n", my_commands->argv[0]);
1218 for (j = my_commands->argc < 5 ? 3 : 5; j < my_commands->argc; j++)
1219 fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
1220 my_commands->argv[0], my_commands->argv[j]);
1222 else if (pg_strcasecmp(my_commands->argv[0], "sleep") == 0)
1224 if (my_commands->argc < 2)
1226 fprintf(stderr, "%s: missing argument\n", my_commands->argv[0]);
1230 if (my_commands->argc >= 3)
1232 if (pg_strcasecmp(my_commands->argv[2], "us") != 0 &&
1233 pg_strcasecmp(my_commands->argv[2], "ms") != 0 &&
1234 pg_strcasecmp(my_commands->argv[2], "s"))
1236 fprintf(stderr, "%s: unknown time unit '%s' - must be us, ms or s\n",
1237 my_commands->argv[0], my_commands->argv[2]);
1242 for (j = 3; j < my_commands->argc; j++)
1243 fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
1244 my_commands->argv[0], my_commands->argv[j]);
1248 fprintf(stderr, "Invalid command %s\n", my_commands->argv[0]);
1254 my_commands->type = SQL_COMMAND;
1259 if ((my_commands->argv[0] = strdup(p)) == NULL)
1261 my_commands->argc++;
1263 case QUERY_EXTENDED:
1264 case QUERY_PREPARED:
1265 if (!parseQuery(my_commands, p))
1277 process_file(char *filename)
1279 #define COMMANDS_ALLOC_NUM 128
1281 Command **my_commands;
1287 if (num_files >= MAX_FILES)
1289 fprintf(stderr, "Up to only %d SQL files are allowed\n", MAX_FILES);
1293 alloc_num = COMMANDS_ALLOC_NUM;
1294 my_commands = (Command **) malloc(sizeof(Command *) * alloc_num);
1295 if (my_commands == NULL)
1298 if (strcmp(filename, "-") == 0)
1300 else if ((fd = fopen(filename, "r")) == NULL)
1302 fprintf(stderr, "%s: %s\n", filename, strerror(errno));
1308 while (fgets(buf, sizeof(buf), fd) != NULL)
1314 while (isspace((unsigned char) buf[i]))
1317 if (buf[i] != '\0' && strncmp(&buf[i], "--", 2) != 0)
1319 commands = process_commands(&buf[i]);
1320 if (commands == NULL)
1329 my_commands[lineno] = commands;
1332 if (lineno >= alloc_num)
1334 alloc_num += COMMANDS_ALLOC_NUM;
1335 my_commands = realloc(my_commands, sizeof(Command *) * alloc_num);
1336 if (my_commands == NULL)
1345 my_commands[lineno] = NULL;
1347 sql_files[num_files++] = my_commands;
1353 process_builtin(char *tb)
1355 #define COMMANDS_ALLOC_NUM 128
1357 Command **my_commands;
1365 alloc_num = COMMANDS_ALLOC_NUM;
1366 my_commands = (Command **) malloc(sizeof(Command *) * alloc_num);
1367 if (my_commands == NULL)
1378 while (*tb && *tb != '\n')
1389 commands = process_commands(buf);
1390 if (commands == NULL)
1395 my_commands[lineno] = commands;
1398 if (lineno >= alloc_num)
1400 alloc_num += COMMANDS_ALLOC_NUM;
1401 my_commands = realloc(my_commands, sizeof(Command *) * alloc_num);
1402 if (my_commands == NULL)
1409 my_commands[lineno] = NULL;
1414 /* print out results */
1417 int ttype, CState * state,
1418 struct timeval * start_time, struct timeval * end_time)
1423 int normal_xacts = 0;
1426 for (i = 0; i < nclients; i++)
1427 normal_xacts += state[i].cnt;
1429 t1 = (end_time->tv_sec - start_time->tv_sec) * 1000000.0 + (end_time->tv_usec - start_time->tv_usec);
1430 t1 = normal_xacts * 1000000.0 / t1;
1432 t2 = (end_time->tv_sec - start_time->tv_sec - conn_total_time.tv_sec) * 1000000.0 +
1433 (end_time->tv_usec - start_time->tv_usec - conn_total_time.tv_usec);
1434 t2 = normal_xacts * 1000000.0 / t2;
1437 s = "TPC-B (sort of)";
1438 else if (ttype == 2)
1439 s = "Update only accounts";
1440 else if (ttype == 1)
1445 printf("transaction type: %s\n", s);
1446 printf("scaling factor: %d\n", scale);
1447 printf("query mode: %s\n", QUERYMODE[querymode]);
1448 printf("number of clients: %d\n", nclients);
1451 printf("number of transactions per client: %d\n", nxacts);
1452 printf("number of transactions actually processed: %d/%d\n",
1453 normal_xacts, nxacts * nclients);
1457 printf("duration: %d s\n", duration);
1458 printf("number of transactions actually processed: %d\n",
1461 printf("tps = %f (including connections establishing)\n", t1);
1462 printf("tps = %f (excluding connections establishing)\n", t2);
1467 main(int argc, char **argv)
1470 int is_init_mode = 0; /* initialize mode? */
1471 int is_no_vacuum = 0; /* no vacuum at all before testing? */
1472 int do_vacuum_accounts = 0; /* do vacuum accounts before testing? */
1473 int debug = 0; /* debug flag */
1474 int ttype = 0; /* transaction type. 0: TPC-B, 1: SELECT only,
1475 * 2: skip update of branches and tellers */
1476 char *filename = NULL;
1477 bool scale_given = false;
1479 CState *state; /* status of clients */
1481 struct timeval start_time; /* start up time */
1482 struct timeval end_time; /* end time */
1487 int nsocks; /* return from select(2) */
1488 int maxsock; /* max socket number to be waited */
1490 struct timeval timeout;
1493 #ifdef HAVE_GETRLIMIT
1504 /* stderr is buffered on Win32. */
1505 setvbuf(stderr, NULL, _IONBF, 0);
1508 if ((env = getenv("PGHOST")) != NULL && *env != '\0')
1510 if ((env = getenv("PGPORT")) != NULL && *env != '\0')
1512 else if ((env = getenv("PGUSER")) != NULL && *env != '\0')
1515 state = (CState *) malloc(sizeof(CState));
1518 fprintf(stderr, "Couldn't allocate memory for state\n");
1522 memset(state, 0, sizeof(*state));
1524 while ((c = getopt(argc, argv, "ih:nvp:dSNc:Cs:t:T:U:lf:D:F:M:")) != -1)
1538 do_vacuum_accounts++;
1553 nclients = atoi(optarg);
1554 if (nclients <= 0 || nclients > MAXCLIENTS)
1556 fprintf(stderr, "invalid number of clients: %d\n", nclients);
1559 #ifdef HAVE_GETRLIMIT
1560 #ifdef RLIMIT_NOFILE /* most platforms use RLIMIT_NOFILE */
1561 if (getrlimit(RLIMIT_NOFILE, &rlim) == -1)
1562 #else /* but BSD doesn't ... */
1563 if (getrlimit(RLIMIT_OFILE, &rlim) == -1)
1564 #endif /* RLIMIT_NOFILE */
1566 fprintf(stderr, "getrlimit failed: %s\n", strerror(errno));
1569 if (rlim.rlim_cur <= (nclients + 2))
1571 fprintf(stderr, "You need at least %d open files but you are only allowed to use %ld.\n", nclients + 2, (long) rlim.rlim_cur);
1572 fprintf(stderr, "Use limit/ulimit to increase the limit before using pgbench.\n");
1575 #endif /* HAVE_GETRLIMIT */
1582 scale = atoi(optarg);
1585 fprintf(stderr, "invalid scaling factor: %d\n", scale);
1592 fprintf(stderr, "specify either a number of transactions (-t) or a duration (-T), not both.\n");
1595 nxacts = atoi(optarg);
1598 fprintf(stderr, "invalid number of transactions: %d\n", nxacts);
1605 fprintf(stderr, "specify either a number of transactions (-t) or a duration (-T), not both.\n");
1608 duration = atoi(optarg);
1611 fprintf(stderr, "invalid duration: %d\n", duration);
1624 if (process_file(filename) == false || *sql_files[num_files - 1] == NULL)
1631 if ((p = strchr(optarg, '=')) == NULL || p == optarg || *(p + 1) == '\0')
1633 fprintf(stderr, "invalid variable definition: %s\n", optarg);
1638 if (putVariable(&state[0], optarg, p) == false)
1640 fprintf(stderr, "Couldn't allocate memory for variable\n");
1646 fillfactor = atoi(optarg);
1647 if ((fillfactor < 10) || (fillfactor > 100))
1649 fprintf(stderr, "invalid fillfactor: %d\n", fillfactor);
1656 fprintf(stderr, "querymode(-M) should be specifiled before transaction scripts(-f)\n");
1659 for (querymode = 0; querymode < NUM_QUERYMODE; querymode++)
1660 if (strcmp(optarg, QUERYMODE[querymode]) == 0)
1662 if (querymode >= NUM_QUERYMODE)
1664 fprintf(stderr, "invalid querymode(-M): %s\n", optarg);
1676 dbName = argv[optind];
1679 if ((env = getenv("PGDATABASE")) != NULL && *env != '\0')
1681 else if (login != NULL && *login != '\0')
1693 /* Use DEFAULT_NXACTS if neither nxacts nor duration is specified. */
1694 if (nxacts <= 0 && duration <= 0)
1695 nxacts = DEFAULT_NXACTS;
1701 state = (CState *) realloc(state, sizeof(CState) * nclients);
1704 fprintf(stderr, "Couldn't allocate memory for state\n");
1708 memset(state + 1, 0, sizeof(*state) * (nclients - 1));
1710 /* copy any -D switch values to all clients */
1711 for (i = 1; i < nclients; i++)
1715 for (j = 0; j < state[0].nvariables; j++)
1717 if (putVariable(&state[i], state[0].variables[j].name, state[0].variables[j].value) == false)
1719 fprintf(stderr, "Couldn't allocate memory for variable\n");
1730 snprintf(logpath, 64, "pgbench_log.%d", (int) getpid());
1731 LOGFILE = fopen(logpath, "w");
1733 if (LOGFILE == NULL)
1735 fprintf(stderr, "Couldn't open logfile \"%s\": %s", logpath, strerror(errno));
1743 printf("pghost: %s pgport: %s nclients: %d nxacts: %d dbName: %s\n",
1744 pghost, pgport, nclients, nxacts, dbName);
1746 printf("pghost: %s pgport: %s nclients: %d duration: %d dbName: %s\n",
1747 pghost, pgport, nclients, duration, dbName);
1750 /* opening connection... */
1755 if (PQstatus(con) == CONNECTION_BAD)
1757 fprintf(stderr, "Connection to database '%s' failed.\n", dbName);
1758 fprintf(stderr, "%s", PQerrorMessage(con));
1765 * get the scaling factor that should be same as count(*) from
1766 * branches if this is not a custom query
1768 res = PQexec(con, "select count(*) from branches");
1769 if (PQresultStatus(res) != PGRES_TUPLES_OK)
1771 fprintf(stderr, "%s", PQerrorMessage(con));
1774 scale = atoi(PQgetvalue(res, 0, 0));
1777 fprintf(stderr, "count(*) from branches invalid (%d)\n", scale);
1782 /* warn if we override user-given -s switch */
1785 "Scale option ignored, using branches table count = %d\n",
1790 * :scale variables normally get -s or database scale, but don't override
1791 * an explicit -D switch
1793 if (getVariable(&state[0], "scale") == NULL)
1795 snprintf(val, sizeof(val), "%d", scale);
1796 for (i = 0; i < nclients; i++)
1798 if (putVariable(&state[i], "scale", val) == false)
1800 fprintf(stderr, "Couldn't allocate memory for variable\n");
1808 fprintf(stderr, "starting vacuum...");
1809 executeStatement(con, "vacuum branches");
1810 executeStatement(con, "vacuum tellers");
1811 executeStatement(con, "truncate history");
1812 fprintf(stderr, "end.\n");
1814 if (do_vacuum_accounts)
1816 fprintf(stderr, "starting vacuum accounts...");
1817 executeStatement(con, "vacuum analyze accounts");
1818 fprintf(stderr, "end.\n");
1823 /* set random seed */
1824 gettimeofday(&start_time, NULL);
1825 srandom((unsigned int) start_time.tv_usec);
1827 /* get start up time */
1828 gettimeofday(&start_time, NULL);
1830 /* set alarm if duration is specified. */
1834 if (is_connect == 0)
1836 struct timeval t, now;
1838 /* make connections to the database */
1839 for (i = 0; i < nclients; i++)
1842 if ((state[i].con = doConnect()) == NULL)
1845 /* time after connections set up */
1846 gettimeofday(&now, NULL);
1847 diffTime(&now, &start_time, &t);
1848 addTime(&conn_total_time, &t, &conn_total_time);
1851 /* process bultin SQL scripts */
1855 sql_files[0] = process_builtin(tpc_b);
1860 sql_files[0] = process_builtin(select_only);
1865 sql_files[0] = process_builtin(simple_update);
1873 /* send start up queries in async manner */
1874 for (i = 0; i < nclients; i++)
1876 Command **commands = sql_files[state[i].use_file];
1877 int prev_ecnt = state[i].ecnt;
1879 state[i].use_file = getrand(0, num_files - 1);
1880 doCustom(state, i, debug);
1882 if (state[i].ecnt > prev_ecnt && commands[state[i].state]->type == META_COMMAND)
1884 fprintf(stderr, "Client %d aborted in state %d. Execution meta-command failed.\n", i, state[i].state);
1885 remains--; /* I've aborted */
1886 PQfinish(state[i].con);
1887 state[i].con = NULL;
1895 disconnect_all(state);
1897 gettimeofday(&end_time, NULL);
1898 printResults(ttype, state, &start_time, &end_time);
1904 FD_ZERO(&input_mask);
1908 for (i = 0; i < nclients; i++)
1910 Command **commands = sql_files[state[i].use_file];
1912 if (state[i].sleeping)
1915 int sock = PQsocket(state[i].con);
1919 gettimeofday(&now, NULL);
1923 this_usec = (state[i].until.tv_sec - now.tv_sec) * 1000000 +
1924 state[i].until.tv_usec - now.tv_usec;
1926 if (this_usec > 0 && (min_usec == 0 || this_usec < min_usec))
1927 min_usec = this_usec;
1929 FD_SET(sock, &input_mask);
1933 else if (state[i].con && commands[state[i].state]->type != META_COMMAND)
1935 int sock = PQsocket(state[i].con);
1939 disconnect_all(state);
1942 FD_SET(sock, &input_mask);
1952 timeout.tv_sec = min_usec / 1000000;
1953 timeout.tv_usec = min_usec % 1000000;
1955 nsocks = select(maxsock + 1, &input_mask, (fd_set *) NULL,
1956 (fd_set *) NULL, &timeout);
1959 nsocks = select(maxsock + 1, &input_mask, (fd_set *) NULL,
1960 (fd_set *) NULL, (struct timeval *) NULL);
1965 /* must be something wrong */
1966 disconnect_all(state);
1967 fprintf(stderr, "select failed: %s\n", strerror(errno));
1971 else if (nsocks == 0)
1973 fprintf(stderr, "select timeout\n");
1974 for (i = 0; i < nclients; i++)
1976 fprintf(stderr, "client %d:state %d cnt %d ecnt %d listen %d\n",
1977 i, state[i].state, state[i].cnt, state[i].ecnt, state[i].listen);
1984 /* ok, backend returns reply */
1985 for (i = 0; i < nclients; i++)
1987 Command **commands = sql_files[state[i].use_file];
1988 int prev_ecnt = state[i].ecnt;
1990 if (state[i].con && (FD_ISSET(PQsocket(state[i].con), &input_mask)
1991 || commands[state[i].state]->type == META_COMMAND))
1993 doCustom(state, i, debug);
1996 if (state[i].ecnt > prev_ecnt && commands[state[i].state]->type == META_COMMAND)
1998 fprintf(stderr, "Client %d aborted in state %d. Execution of meta-command failed.\n", i, state[i].state);
1999 remains--; /* I've aborted */
2000 PQfinish(state[i].con);
2001 state[i].con = NULL;
2009 * Support for duration option: set timer_exceeded after so many seconds.
2015 handle_sig_alarm(SIGNAL_ARGS)
2017 timer_exceeded = true;
2021 setalarm(int seconds)
2023 pqsignal(SIGALRM, handle_sig_alarm);
2029 static VOID CALLBACK
2030 win32_timer_callback(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
2032 timer_exceeded = true;
2036 setalarm(int seconds)
2041 /* This function will be called at most once, so we can cheat a bit. */
2042 queue = CreateTimerQueue();
2043 if (seconds > ((DWORD)-1) / 1000 ||
2044 !CreateTimerQueueTimer(&timer, queue,
2045 win32_timer_callback, NULL, seconds * 1000, 0,
2046 WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE))
2048 fprintf(stderr, "Failed to set timer\n");