Commit 9b19abd7 authored by Tatsuo Ishii's avatar Tatsuo Ishii

Add -f option which enables to read SQL commands from a file.

Patches Contributed by Tomoaki Sato.
parent 8928d4d6
pgbench README 2003/11/26 Tatsuo Ishii (t-ishii@sra.co.jp) pgbench README 2005/09/29 Tatsuo Ishii
o What is pgbench? o What is pgbench?
...@@ -34,16 +34,8 @@ o features of pgbench ...@@ -34,16 +34,8 @@ o features of pgbench
o How to install pgbench o How to install pgbench
(1) Configure and build the standard Postgres distribution. $make
$make install
You can get away with just running configure at the top level
and doing "make all" in src/interfaces/libpq.
(2) Run make in this directory.
You will see an executable file "pgbench". You can run it here,
or install it with the standard Postgres programs by doing
"make install".
o How to use pgbench? o How to use pgbench?
...@@ -124,6 +116,15 @@ o options ...@@ -124,6 +116,15 @@ o options
-S -S
Perform select only transactions instead of TPC-B. Perform select only transactions instead of TPC-B.
-N Do not update "branches" and "tellers". This will
avoid heavy update contention on branches and tellers,
while it will not make pgbench supporting TPC-B like
transactions.
-f filename
Read transaction script from file. Detailed
explanation will appear later.
-C -C
Establish connection for each transaction, rather than Establish connection for each transaction, rather than
doing it just once at beginning of pgbench in the normal doing it just once at beginning of pgbench in the normal
...@@ -158,12 +159,58 @@ o What is the "transaction" actually performed in pgbench? ...@@ -158,12 +159,58 @@ o What is the "transaction" actually performed in pgbench?
(7) end; (7) end;
o -f option
This supports for reading transaction script from a specified
file. This file should include SQL commands in each line. SQL
command consists of multiple lines are not supported. Empty lines
and lines begging with "--" will be ignored.
SQL commands can include "meta command" which begins with "\" (back
slash). A meta command takes some arguments separted by white
spaces. Currently following meta command is supported:
\setrandom name min max
assign random integer to name between min and max
example:
\setrandom aid 1 100000
variables can be reffered to in SQL comands by adding ":" in front
of the varible name.
example:
SELECT abalance FROM accounts WHERE aid = :aid
For example, TPC-B like benchmark can be defined as follows(scaling
factor = 1):
\setrandom aid 1 100000
\setrandom bid 1 1
\setrandom tid 1 10
\setrandom delta 1 10000
BEGIN
UPDATE accounts SET abalance = abalance + :delta WHERE aid = :aid
SELECT abalance FROM accounts WHERE aid = :aid
UPDATE tellers SET tbalance = tbalance + :delta WHERE tid = :tid
UPDATE branches SET bbalance = bbalance + :delta WHERE bid = :bid
INSERT INTO history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, 'now')
END
o License? o License?
Basically it is same as BSD license. See pgbench.c for more details. Basically it is same as BSD license. See pgbench.c for more details.
o History o History
2005/09/29
* add -f option. contributed by Tomoaki Sato.
[updation records were missing]
2003/11/26 2003/11/26
* create indexes after data insertion to reduce time. * create indexes after data insertion to reduce time.
patch from Yutaka Tanida. patch from Yutaka Tanida.
......
pgbench README 2003/11/26 Tatsuo Ishii (t-ishii@sra.co.jp) pgbench README 2005/09/29 Tatsuo Ishii
$B"#(Bpgbench $B$H$O!)(B $B"#(Bpgbench $B$H$O!)(B
pgbench $B$O(B TPC-B$B$K;w$?%Y%s%A%^!<%/%F%9%H$r9T$J$&%W%m%0%i%`$G$9!%:#$N$H(B pgbench $B$O%Y%s%A%^!<%/%F%9%H$r9T$J$&%W%m%0%i%`$G$9!%:#$N$H$3$m(B
$B$3$m(B PostgreSQL $B@lMQ$G$9!%(B PostgreSQL $B@lMQ$G$9!%(B
pgbench $B$O(B select/update/insert $B$r4^$`%H%i%s%6%/%7%g%s$r<B9T$7!$A4BN$N(B pgbench $B$O(B select/update/insert $B$r4^$`%H%i%s%6%/%7%g%s$r<B9T$7!$A4BN$N(B
$B<B9T;~4V$H<B:]$K40N;$7$?%H%i%s%6%/%7%g%s$N?t$+$i(B 1 $BIC4V$K<B9T$G$-$?%H(B $B<B9T;~4V$H<B:]$K40N;$7$?%H%i%s%6%/%7%g%s$N?t$+$i(B 1 $BIC4V$K<B9T$G$-$?%H(B
...@@ -31,16 +31,12 @@ o pgbench $B$O(B libpq $B$NHsF14|=hM}5!G=$r;H$C$F%^%k%A%f!<%64D6-$r%7%_%e%l!< ...@@ -31,16 +31,12 @@ o pgbench $B$O(B libpq $B$NHsF14|=hM}5!G=$r;H$C$F%^%k%A%f!<%64D6-$r%7%_%e%l!<
$B"#(Bpgbench $B$N%$%s%9%H!<%k(B $B"#(Bpgbench $B$N%$%s%9%H!<%k(B
(1) PostgreSQL$B$r(Bconfigure$B!$%3%s%Q%$%k$7$^$9!%(Bpgbench$B$N%$%s%9%H!<%k$@$1(B PostgreSQL$B$r%3%s%Q%$%k!$%$%s%9%H!<%k$7$?8e(B
$B$,L\E*$G$"$l$P!$(BPostgreSQL$B$N$9$Y$F$r%3%s%Q%$%k$9$kI,MW$O$"$j$^$;$s!%(B
PostgreSQL$B%=!<%9$N%H%C%W%G%#%l%/%H%j$G(Bconfigure$B$r$7$?8e!$(B
src/interface/libpq $B$G(B "make all" $B$r<B9T$9$l$P=`Hw40N;$G$9!%(B
(2) $B$3$N%G%#%l%/%H%j$G(B make $B$r<B9T$7$^$9!%$=$&$9$k$H!$(B"pgbench" $B$H$$$&(B $ make
$B<B9T%W%m%0%i%`$,$G$-$^$9!%$=$N$^$^<B9T$7$F$b9=$$$^$;$s$7!$(B"make $ make install
install" $B$r<B9T$7$F(B PostgreSQL $B$NI8=`<B9T%W%m%0%i%`%G%#%l%/%H%j(B
($B%G%U%)%k%H$G$O(B /usr/local/pgsql/bin) $B$K%$%s%9%H!<%k$9$k$3$H$b$G$-(B $B$H$7$^$9!%(B
$B$^$9!%(B
$B"#(Bpgbench $B$N;H$$J}(B $B"#(Bpgbench $B$N;H$$J}(B
...@@ -104,6 +100,12 @@ pgbench $B$K$O$$$m$$$m$J%*%W%7%g%s$,$"$j$^$9!%(B ...@@ -104,6 +100,12 @@ pgbench $B$K$O$$$m$$$m$J%*%W%7%g%s$,$"$j$^$9!%(B
$B$OE,9g$7$J$/$J$j$^$9$,!$$h$j8=<BE*$JIi2Y$r%F%9%H$9$k$3(B $B$OE,9g$7$J$/$J$j$^$9$,!$$h$j8=<BE*$JIi2Y$r%F%9%H$9$k$3(B
$B$H$,$G$-$^$9!%(B $B$H$,$G$-$^$9!%(B
-f filename $B%H%i%s%6%/%7%g%s$NFbMF$,5-=R$5$l$?%U%!%$%kL>$r;XDj$7$^(B
$B$9!%$3$N%*%W%7%g%s$r;XDj$9$k$H!$%U%!%$%k$K5-=R$5$l$?Fb(B
$BMF$N%H%i%s%6%/%7%g%s$r<B9T$7$^$9!%$J$*!$%Y%s%A%^!<%/$N(B
$BBP>]$H$J$k%G!<%?%Y!<%9$O$"$i$+$8$a=i4|2=$7$F$*$/I,MW$,(B
$B$"$j$^$9!%F~NO%U%)!<%^%C%H$K$D$$$F$O8e=R$7$^$9!%(B
-C $B$3$N%*%W%7%g%s$r;XDj$9$k$H!$:G=i$K3NN)$7$?%3%M%/%7%g%s(B -C $B$3$N%*%W%7%g%s$r;XDj$9$k$H!$:G=i$K3NN)$7$?%3%M%/%7%g%s(B
$B$r;H$$2s$9$N$G$O$J$/!$3F%H%i%s%6%/%7%g%s$4$H$K(BDB$B$X$N@\(B $B$r;H$$2s$9$N$G$O$J$/!$3F%H%i%s%6%/%7%g%s$4$H$K(BDB$B$X$N@\(B
$BB3$r9T$$$^$9!%%3%M%/%7%g%s$N%*!<%P!<$X%C%I$rB,Dj$9$k$N(B $BB3$r9T$$$^$9!%%3%M%/%7%g%s$N%*!<%P!<$X%C%I$rB,Dj$9$k$N(B
...@@ -176,6 +178,52 @@ pgbench $B$G$O!$0J2<$N%7!<%1%s%9$rA4It40N;$7$F(B1$B%H%i%s%6%/%7%g%s$H?t$($F( ...@@ -176,6 +178,52 @@ pgbench $B$G$O!$0J2<$N%7!<%1%s%9$rA4It40N;$7$F(B1$B%H%i%s%6%/%7%g%s$H?t$($F(
(7) end; (7) end;
$B"#F~NO%U%!%$%k$N%U%)!<%^%C%H(B
pgbench $B$G$O!$(B-f $B%*%W%7%g%s$r;XDj$7$F%H%i%s%6%/%7%g%s$K4^$^$l$k(B SQL $B%3(B
$B%^%s%I$NFbMF$r5-=R$7$?%U%!%$%k$rFI$_9~$`$3$H$,$G$-$^$9!%F~NO%U%!%$%k$K(B
$B$O(B 1 $B9T$K$D$-(B 1 $B$D$N%3%^%s%I$r5-=R$7$^$9!%6u9T$OL5;k$5$l!$Fs=E%O%$%U%s(B
$B$G;O$^$k9T$O%3%a%s%H$r0UL#$7$^$9!%(B
$B%3%^%s%I$K$O!$(BSQL $B%3%^%s%I$K2C$(!$%P%C%/%9%i%C%7%e$G;O$^$k%a%?%3%^%s%I(B
$B$r5-=R$G$-$^$9!%%a%?%3%^%s%I$O(B pgbench $B<+?H$K$h$C$F<B9T$5$l$^$9!%%a%?(B
$B%3%^%s%I$N7A<0$O%P%C%/%9%i%C%7%e!$$=$ND>8e$K%3%^%s%I$NF0;l!$$=$N<!$K0z(B
$B?t$,B3$-$^$9!%F0;l%3%^%s%I$H0z?t!$$^$?$=$l$>$l$N0z?t$O6uGrJ8;z$K$h$C$F(B
$B6h@Z$i$l$^$9!%(B
$B8=:_$N$H$3$m!$0J2<$N%a%?%3%^%s%I$,Dj5A$5$l$F$$$^$9!%(B
\setrandom name min max
$B:G>.CM(B min $B$H:GBgCM(B max $B$N4V$NCM$r<h$kMp?t$r!$(Bname $BJQ?t$K@_Dj(B
$B$7$^$9!%(B
$BJQ?t$KMp?t$r@_Dj$9$k$K$O!$(B\setrandom $B%a%?%3%^%s%I$r;HMQ$7$F0J2<$N$h$&(B
$B$K5-=R$7$^$9!%(B
\setrandom aid 1 100000
$B$3$l$O!$JQ?t(B aid $B$K(B 1 $B$+$i(B 100000 $B$N4V$NMp?t$r@_Dj$7$^$9!%$^$?!$JQ?t$N(B
$BCM$r(B SQL $B%3%^%s%I$KKd$a9~$`$K$O!$0J2<$N$h$&$K$=$NL>A0$NA0$K%3%m%s$rIU(B
$B$1$^$9!%(B
SELECT abalance FROM accounts WHERE aid = :aid
$BNc$($P!$(BTCP-B $B$KN`;w$7$?%Y%s%A%^!<%/$r7WB,$9$k$K$O!$0J2<$N$h$&$K%H%i%s(B
$B%6%/%7%g%s$NFbMF$r%U%!%$%k$K5-=R$7!$(B-f $B%*%W%7%g%s$K$h$C$F$=$N%U%!%$%k(B
$B$r;XDj$7$F(B pgbench $B$r<B9T$7$^$9!%(B
\setrandom aid 1 100000
\setrandom bid 1 1
\setrandom tid 1 10
\setrandom delta 1 10000
BEGIN
UPDATE accounts SET abalance = abalance + :delta WHERE aid = :aid
SELECT abalance FROM accounts WHERE aid = :aid
UPDATE tellers SET tbalance = tbalance + :delta WHERE tid = :tid
UPDATE branches SET bbalance = bbalance + :delta WHERE bid = :bid
INSERT INTO history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, 'now')
END
$B"#:n<T$H%i%$%;%s%9>r7o(B $B"#:n<T$H%i%$%;%s%9>r7o(B
pgbench $B$O@P0f(B $BC#IW$K$h$C$F=q$+$l$^$7$?!%%i%$%;%s%9>r7o$O(B pgbench.c $B$N(B pgbench $B$O@P0f(B $BC#IW$K$h$C$F=q$+$l$^$7$?!%%i%$%;%s%9>r7o$O(B pgbench.c $B$N(B
...@@ -184,6 +232,11 @@ pgbench $B$O@P0f(B $BC#IW$K$h$C$F=q$+$l$^$7$?!%%i%$%;%s%9>r7o$O(B pgbench.c ...@@ -184,6 +232,11 @@ pgbench $B$O@P0f(B $BC#IW$K$h$C$F=q$+$l$^$7$?!%%i%$%;%s%9>r7o$O(B pgbench.c
$B"#2~DjMzNr(B $B"#2~DjMzNr(B
2005/09/29
* $B:4F#$5$s$N%Q%C%A$rE,MQ!%(B-f $B%*%W%7%g%s$NDI2C!%(B
[$B$3$N4V$$$m$$$mJQ99$,$"$C$?$h$&$@$,(BREADME$B$O%a%$%s%F%J%s%9$5$l$F$$$J$$(B]
2003/11/26 2003/11/26
* $BC+ED$5$s$N%Q%C%A$rE,MQ!%(Bpgbench -i$B$N:]$K!$8e$+$i<g%-!<$r:n@.(B * $BC+ED$5$s$N%Q%C%A$rE,MQ!%(Bpgbench -i$B$N:]$K!$8e$+$i<g%-!<$r:n@.(B
$B$9$k$h$&$K$7$?!%$3$l$K$h$C$F=i4|2=$N<B9T;~4V$,BgI}$KC;=L$G$-(B $B$9$k$h$&$K$7$?!%$3$l$K$h$C$F=i4|2=$N<B9T;~4V$,BgI}$KC;=L$G$-(B
......
/* /*
* $PostgreSQL: pgsql/contrib/pgbench/pgbench.c,v 1.36 2005/05/24 00:26:40 neilc Exp $ * $PostgreSQL: pgsql/contrib/pgbench/pgbench.c,v 1.37 2005/09/29 13:44:25 ishii Exp $
* *
* pgbench: a simple TPC-B like benchmark program for PostgreSQL * pgbench: a simple TPC-B like benchmark program for PostgreSQL
* written by Tatsuo Ishii * written by Tatsuo Ishii
* *
* Copyright (c) 2000-2004 Tatsuo Ishii * Copyright (c) 2000-2005 Tatsuo Ishii
* *
* Permission to use, copy, modify, and distribute this software and * Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby * its documentation for any purpose and without fee is hereby
...@@ -41,6 +41,9 @@ ...@@ -41,6 +41,9 @@
#include <sys/resource.h> #include <sys/resource.h>
#endif /* ! WIN32 */ #endif /* ! WIN32 */
#include <ctype.h>
#include <search.h>
extern char *optarg; extern char *optarg;
extern int optind; extern int optind;
...@@ -72,6 +75,9 @@ int tps = 1; ...@@ -72,6 +75,9 @@ int tps = 1;
#define ntellers 10 #define ntellers 10
#define naccounts 100000 #define naccounts 100000
#define SQL_COMMAND 1
#define META_COMMAND 2
FILE *LOGFILE = NULL; FILE *LOGFILE = NULL;
bool use_log; /* log transaction latencies to a file */ bool use_log; /* log transaction latencies to a file */
...@@ -89,6 +95,12 @@ char *login = NULL; ...@@ -89,6 +95,12 @@ char *login = NULL;
char *pwd = NULL; char *pwd = NULL;
char *dbName; char *dbName;
typedef struct
{
char *name;
char *value;
} Variable;
typedef struct typedef struct
{ {
PGconn *con; /* connection handle to DB */ PGconn *con; /* connection handle to DB */
...@@ -103,13 +115,23 @@ typedef struct ...@@ -103,13 +115,23 @@ typedef struct
int tid; /* teller id for this transaction */ int tid; /* teller id for this transaction */
int delta; int delta;
int abalance; int abalance;
void *variables;
struct timeval txn_begin; /* used for measuring latencies */ struct timeval txn_begin; /* used for measuring latencies */
} CState; } CState;
typedef struct
{
int type;
int argc;
char **argv;
} Command;
Command **commands = NULL;
static void static void
usage(void) usage(void)
{ {
fprintf(stderr, "usage: pgbench [-h hostname][-p port][-c nclients][-t ntransactions][-s scaling_factor][-n][-C][-v][-S][-N][-l][-U login][-P password][-d][dbname]\n"); fprintf(stderr, "usage: pgbench [-h hostname][-p port][-c nclients][-t ntransactions][-s scaling_factor][-n][-C][-v][-S][-N][-f filename][-l][-U login][-P password][-d][dbname]\n");
fprintf(stderr, "(initialize mode): pgbench -i [-h hostname][-p port][-s scaling_factor][-U login][-P password][-d][dbname]\n"); fprintf(stderr, "(initialize mode): pgbench -i [-h hostname][-p port][-s scaling_factor][-U login][-P password][-d][dbname]\n");
} }
...@@ -190,6 +212,115 @@ check(CState * state, PGresult *res, int n, int good) ...@@ -190,6 +212,115 @@ check(CState * state, PGresult *res, int n, int good)
return (0); /* OK */ return (0); /* OK */
} }
static int
compareVariables(const void *v1, const void *v2)
{
return strcmp(((Variable *)v1)->name, ((Variable *)v2)->name);
}
static char *
getVariable(CState * st, char *name)
{
Variable key = { name }, *var;
var = tfind(&key, &st->variables, compareVariables);
if (var != NULL)
return (*(Variable **)var)->value;
else
return NULL;
}
static int
putVariable(CState * st, char *name, char *value)
{
Variable key = { name }, *var;
var = tfind(&key, &st->variables, compareVariables);
if (var == NULL)
{
if ((var = malloc(sizeof(Variable))) == NULL)
return false;
var->name = NULL;
var->value = NULL;
if ((var->name = strdup(name)) == NULL
|| (var->value = strdup(value)) == NULL
|| tsearch(var, &st->variables, compareVariables) == NULL)
{
free(var->name);
free(var->value);
free(var);
return false;
}
}
else
{
free((*(Variable **)var)->value);
if (((*(Variable **)var)->value = strdup(value)) == NULL)
return false;
}
return true;
}
static char *
assignVariables(CState * st, char *sql)
{
int i, j;
char *p, *name, *val;
void *tmp;
i = 0;
while ((p = strchr(&sql[i], ':')) != NULL)
{
i = j = p - sql;
do
i++;
while (isalnum(sql[i]) != 0 || sql[i] == '_');
if (i == j + 1)
continue;
if ((name = strndup(&sql[j + 1], i - (j + 1))) == NULL)
return NULL;
val = getVariable(st, name);
free(name);
if (val == NULL)
continue;
if (strlen(val) > i - j)
{
tmp = realloc(sql, strlen(sql) - (i - j) + strlen(val) + 1);
if (tmp == NULL)
{
free(sql);
return NULL;
}
sql = tmp;
}
if (strlen(val) != i - j)
memmove(&sql[j + strlen(val)], &sql[i], strlen(&sql[i]) + 1);
strncpy(&sql[j], val, strlen(val));
if (strlen(val) < i - j)
{
tmp = realloc(sql, strlen(sql) + 1);
if (tmp == NULL)
{
free(sql);
return NULL;
}
sql = tmp;
}
i = j + strlen(val);
}
return sql;
}
/* process a transaction */ /* process a transaction */
static void static void
doOne(CState * state, int n, int debug, int ttype) doOne(CState * state, int n, int debug, int ttype)
...@@ -465,6 +596,170 @@ doSelectOnly(CState * state, int n, int debug) ...@@ -465,6 +596,170 @@ doSelectOnly(CState * state, int n, int debug)
} }
} }
static void
doCustom(CState * state, int n, int debug)
{
PGresult *res;
CState *st = &state[n];
if (st->listen)
{ /* are we receiver? */
if (commands[st->state]->type == SQL_COMMAND)
{
if (debug)
fprintf(stderr, "client %d receiving\n", n);
if (!PQconsumeInput(st->con))
{ /* there's something wrong */
fprintf(stderr, "Client %d aborted in state %d. Probably the backend died while processing.\n", n, st->state);
remains--; /* I've aborted */
PQfinish(st->con);
st->con = NULL;
return;
}
if (PQisBusy(st->con))
return; /* don't have the whole result yet */
}
/*
* transaction finished: record the time it took in the
* log
*/
if (use_log && commands[st->state + 1] == NULL)
{
double diff;
struct timeval now;
gettimeofday(&now, NULL);
diff = (int) (now.tv_sec - st->txn_begin.tv_sec) * 1000000.0 +
(int) (now.tv_usec - st->txn_begin.tv_usec);
fprintf(LOGFILE, "%d %d %.0f\n", st->id, st->cnt, diff);
}
if (commands[st->state]->type == SQL_COMMAND)
{
res = PQgetResult(st->con);
if (strncasecmp(commands[st->state]->argv[0], "select", 6) != 0)
{
if (check(state, res, n, PGRES_COMMAND_OK))
return;
}
else
{
if (check(state, res, n, PGRES_TUPLES_OK))
return;
}
PQclear(res);
discard_response(st);
}
if (commands[st->state + 1] == NULL)
{
if (is_connect)
{
PQfinish(st->con);
st->con = NULL;
}
if (++st->cnt >= nxacts)
{
remains--; /* I'm done */
if (st->con != NULL)
{
PQfinish(st->con);
st->con = NULL;
}
return;
}
}
/* increment state counter */
st->state++;
if (commands[st->state] == NULL)
st->state = 0;
}
if (st->con == NULL)
{
if ((st->con = doConnect()) == NULL)
{
fprintf(stderr, "Client %d aborted in establishing connection.\n",
n);
remains--; /* I've aborted */
PQfinish(st->con);
st->con = NULL;
return;
}
}
if (use_log && st->state == 0)
gettimeofday(&(st->txn_begin), NULL);
if (commands[st->state]->type == SQL_COMMAND)
{
char *sql;
if ((sql = strdup(commands[st->state]->argv[0])) == NULL
|| (sql = assignVariables(st, sql)) == NULL)
{
fprintf(stderr, "out of memory\n");
st->ecnt++;
return;
}
if (debug)
fprintf(stderr, "client %d sending %s\n", n, sql);
if (PQsendQuery(st->con, sql) == 0)
{
if (debug)
fprintf(stderr, "PQsendQuery(%s)failed\n", sql);
st->ecnt++;
}
else
{
st->listen++; /* flags that should be listened */
}
}
else if (commands[st->state]->type == META_COMMAND)
{
int argc = commands[st->state]->argc, i;
char **argv = commands[st->state]->argv;
if (debug)
{
fprintf(stderr, "client %d executing \\%s", n, argv[0]);
for (i = 1; i < argc; i++)
fprintf(stderr, " %s", argv[i]);
fprintf(stderr, "\n");
}
if (strcasecmp(argv[0], "setrandom") == 0)
{
char *val;
if ((val = malloc(strlen(argv[3]) + 1)) == NULL)
{
fprintf(stderr, "%s: out of memory\n", argv[0]);
st->ecnt++;
return;
}
sprintf(val, "%d", getrand(atoi(argv[2]), atoi(argv[3])));
if (putVariable(st, argv[1], val) == false)
{
fprintf(stderr, "%s: out of memory\n", argv[0]);
free(val);
st->ecnt++;
return;
}
free(val);
st->listen++;
}
}
}
/* discard connections */ /* discard connections */
static void static void
disconnect_all(CState * state) disconnect_all(CState * state)
...@@ -644,6 +939,160 @@ init(void) ...@@ -644,6 +939,160 @@ init(void)
PQfinish(con); PQfinish(con);
} }
static int
process_file(char *filename)
{
const char delim[] = " \f\n\r\t\v";
FILE *fd;
int lineno, i, j;
char buf[BUFSIZ], *p, *tok;
void *tmp;
if (strcmp(filename, "-") == 0)
fd = stdin;
else if ((fd = fopen(filename, "r")) == NULL)
{
fprintf(stderr, "%s: %s\n", strerror(errno), filename);
return false;
}
fprintf(stderr, "processing file...\n");
lineno = 1;
i = 0;
while (fgets(buf, sizeof(buf), fd) != NULL)
{
if ((p = strchr(buf, '\n')) != NULL)
*p = '\0';
p = buf;
while (isspace(*p))
p++;
if (*p == '\0' || strncmp(p, "--", 2) == 0)
{
lineno++;
continue;
}
if ((tmp = realloc(commands, sizeof(Command *) * (i + 1))) == NULL)
{
i--;
goto error;
}
commands = tmp;
if ((commands[i] = malloc(sizeof(Command))) == NULL)
goto error;
commands[i]->argv = NULL;
commands[i]->argc = 0;
if (*p == '\\')
{
commands[i]->type = META_COMMAND;
j = 0;
tok = strtok(++p, delim);
while (tok != NULL)
{
tmp = realloc(commands[i]->argv, sizeof(char *) * (j + 1));
if (tmp == NULL)
goto error;
commands[i]->argv = tmp;
if ((commands[i]->argv[j] = strdup(tok)) == NULL)
goto error;
commands[i]->argc++;
j++;
tok = strtok(NULL, delim);
}
if (strcasecmp(commands[i]->argv[0], "setrandom") == 0)
{
int min, max;
if (commands[i]->argc < 4)
{
fprintf(stderr, "%s: %d: \\%s: missing argument\n", filename, lineno, commands[i]->argv[0]);
goto error;
}
for (j = 4; j < commands[i]->argc; j++)
fprintf(stderr, "%s: %d: \\%s: extra argument \"%s\" ignored\n", filename, lineno, commands[i]->argv[0], commands[i]->argv[j]);
if ((min = atoi(commands[i]->argv[2])) < 0)
{
fprintf(stderr, "%s: %d: \\%s: invalid minimum number %s\n", filename, lineno, commands[i]->argv[0], commands[i]->argv[2]);
goto error;
}
if ((max = atoi(commands[i]->argv[3])) < min || max > RAND_MAX)
{
fprintf(stderr, "%s: %d: \\%s: invalid maximum number %s\n", filename, lineno, commands[i]->argv[0], commands[i]->argv[3]);
goto error;
}
}
else
{
fprintf(stderr, "%s: %d: invalid command \\%s\n", filename, lineno, commands[i]->argv[0]);
goto error;
}
}
else
{
commands[i]->type = SQL_COMMAND;
if ((commands[i]->argv = malloc(sizeof(char *))) == NULL)
goto error;
if ((commands[i]->argv[0] = strdup(p)) == NULL)
goto error;
commands[i]->argc++;
}
i++;
lineno++;
}
fclose(fd);
if ((tmp = realloc(commands, sizeof(Command *) * (i + 1))) == NULL)
goto error;
commands = tmp;
commands[i] = NULL;
return true;
error:
if (errno == ENOMEM)
fprintf(stderr, "%s: %d: out of memory\n", filename, lineno);
fclose(fd);
if (commands == NULL)
return false;
while (i >= 0)
{
if (commands[i] != NULL)
{
for (j = 0; j < commands[i]->argc; j++)
free(commands[i]->argv[j]);
free(commands[i]->argv);
free(commands[i]);
}
i--;
}
free(commands);
return false;
}
/* print out results */ /* print out results */
static void static void
printResults( printResults(
...@@ -670,8 +1119,10 @@ printResults( ...@@ -670,8 +1119,10 @@ printResults(
s = "TPC-B (sort of)"; s = "TPC-B (sort of)";
else if (ttype == 2) else if (ttype == 2)
s = "Update only accounts"; s = "Update only accounts";
else else if (ttype == 1)
s = "SELECT only"; s = "SELECT only";
else
s = "Custom query";
printf("transaction type: %s\n", s); printf("transaction type: %s\n", s);
printf("scaling factor: %d\n", tps); printf("scaling factor: %d\n", tps);
...@@ -695,6 +1146,7 @@ main(int argc, char **argv) ...@@ -695,6 +1146,7 @@ main(int argc, char **argv)
int ttype = 0; /* transaction type. 0: TPC-B, 1: SELECT int ttype = 0; /* transaction type. 0: TPC-B, 1: SELECT
* only, 2: skip update of branches and * only, 2: skip update of branches and
* tellers */ * tellers */
char *filename = NULL;
static CState *state; /* status of clients */ static CState *state; /* status of clients */
...@@ -724,7 +1176,7 @@ main(int argc, char **argv) ...@@ -724,7 +1176,7 @@ main(int argc, char **argv)
else if ((env = getenv("PGUSER")) != NULL && *env != '\0') else if ((env = getenv("PGUSER")) != NULL && *env != '\0')
login = env; login = env;
while ((c = getopt(argc, argv, "ih:nvp:dc:t:s:U:P:CNSl")) != -1) while ((c = getopt(argc, argv, "ih:nvp:dc:t:s:U:P:CNSlf:")) != -1)
{ {
switch (c) switch (c)
{ {
...@@ -806,6 +1258,10 @@ main(int argc, char **argv) ...@@ -806,6 +1258,10 @@ main(int argc, char **argv)
case 'l': case 'l':
use_log = true; use_log = true;
break; break;
case 'f':
ttype = 3;
filename = optarg;
break;
default: default:
usage(); usage();
exit(1); exit(1);
...@@ -868,74 +1324,83 @@ main(int argc, char **argv) ...@@ -868,74 +1324,83 @@ main(int argc, char **argv)
exit(1); exit(1);
} }
/* if (ttype == 3)
* get the scaling factor that should be same as count(*) from
* branches...
*/
res = PQexec(con, "select count(*) from branches");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{ {
fprintf(stderr, "%s", PQerrorMessage(con)); PQfinish(con);
exit(1); if (process_file(filename) == false)
} exit(1);
tps = atoi(PQgetvalue(res, 0, 0));
if (tps < 0)
{
fprintf(stderr, "count(*) from branches invalid (%d)\n", tps);
exit(1);
} }
PQclear(res); else
if (!is_no_vacuum)
{ {
fprintf(stderr, "starting vacuum..."); /*
res = PQexec(con, "vacuum branches"); * get the scaling factor that should be same as count(*) from
if (PQresultStatus(res) != PGRES_COMMAND_OK) * branches...
*/
res = PQexec(con, "select count(*) from branches");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{ {
fprintf(stderr, "%s", PQerrorMessage(con)); fprintf(stderr, "%s", PQerrorMessage(con));
exit(1); exit(1);
} }
PQclear(res); tps = atoi(PQgetvalue(res, 0, 0));
if (tps < 0)
res = PQexec(con, "vacuum tellers");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{ {
fprintf(stderr, "%s", PQerrorMessage(con)); fprintf(stderr, "count(*) from branches invalid (%d)\n", tps);
exit(1); exit(1);
} }
PQclear(res); PQclear(res);
res = PQexec(con, "delete from history"); if (!is_no_vacuum)
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{ {
fprintf(stderr, "%s", PQerrorMessage(con)); fprintf(stderr, "starting vacuum...");
exit(1); res = PQexec(con, "vacuum branches");
} if (PQresultStatus(res) != PGRES_COMMAND_OK)
PQclear(res); {
res = PQexec(con, "vacuum history"); fprintf(stderr, "%s", PQerrorMessage(con));
if (PQresultStatus(res) != PGRES_COMMAND_OK) exit(1);
{ }
fprintf(stderr, "%s", PQerrorMessage(con)); PQclear(res);
exit(1);
}
PQclear(res);
fprintf(stderr, "end.\n"); res = PQexec(con, "vacuum tellers");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "%s", PQerrorMessage(con));
exit(1);
}
PQclear(res);
if (is_full_vacuum) res = PQexec(con, "delete from history");
{ if (PQresultStatus(res) != PGRES_COMMAND_OK)
fprintf(stderr, "starting full vacuum..."); {
res = PQexec(con, "vacuum analyze accounts"); fprintf(stderr, "%s", PQerrorMessage(con));
exit(1);
}
PQclear(res);
res = PQexec(con, "vacuum history");
if (PQresultStatus(res) != PGRES_COMMAND_OK) if (PQresultStatus(res) != PGRES_COMMAND_OK)
{ {
fprintf(stderr, "%s", PQerrorMessage(con)); fprintf(stderr, "%s", PQerrorMessage(con));
exit(1); exit(1);
} }
PQclear(res); PQclear(res);
fprintf(stderr, "end.\n"); fprintf(stderr, "end.\n");
if (is_full_vacuum)
{
fprintf(stderr, "starting full vacuum...");
res = PQexec(con, "vacuum analyze accounts");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "%s", PQerrorMessage(con));
exit(1);
}
PQclear(res);
fprintf(stderr, "end.\n");
}
} }
PQfinish(con);
} }
PQfinish(con);
/* set random seed */ /* set random seed */
gettimeofday(&tv1, NULL); gettimeofday(&tv1, NULL);
...@@ -965,6 +1430,8 @@ main(int argc, char **argv) ...@@ -965,6 +1430,8 @@ main(int argc, char **argv)
doOne(state, i, debug, ttype); doOne(state, i, debug, ttype);
else if (ttype == 1) else if (ttype == 1)
doSelectOnly(state, i, debug); doSelectOnly(state, i, debug);
else if (ttype == 3)
doCustom(state, i, debug);
} }
for (;;) for (;;)
...@@ -982,16 +1449,16 @@ main(int argc, char **argv) ...@@ -982,16 +1449,16 @@ main(int argc, char **argv)
FD_ZERO(&input_mask); FD_ZERO(&input_mask);
maxsock = 0; maxsock = -1;
for (i = 0; i < nclients; i++) for (i = 0; i < nclients; i++)
{ {
if (state[i].con) if (state[i].con &&
(ttype != 3 || commands[state[i].state]->type != META_COMMAND))
{ {
int sock = PQsocket(state[i].con); int sock = PQsocket(state[i].con);
if (sock < 0) if (sock < 0)
{ {
fprintf(stderr, "Client %d: PQsocket failed\n", i);
disconnect_all(state); disconnect_all(state);
exit(1); exit(1);
} }
...@@ -1001,36 +1468,43 @@ main(int argc, char **argv) ...@@ -1001,36 +1468,43 @@ main(int argc, char **argv)
} }
} }
if ((nsocks = select(maxsock + 1, &input_mask, (fd_set *) NULL, if (maxsock != -1)
(fd_set *) NULL, (struct timeval *) NULL)) < 0)
{ {
if (errno == EINTR) if ((nsocks = select(maxsock + 1, &input_mask, (fd_set *) NULL,
continue; (fd_set *) NULL, (struct timeval *) NULL)) < 0)
/* must be something wrong */
disconnect_all(state);
fprintf(stderr, "select failed: %s\n", strerror(errno));
exit(1);
}
else if (nsocks == 0)
{ /* timeout */
fprintf(stderr, "select timeout\n");
for (i = 0; i < nclients; i++)
{ {
fprintf(stderr, "client %d:state %d cnt %d ecnt %d listen %d\n", if (errno == EINTR)
i, state[i].state, state[i].cnt, state[i].ecnt, state[i].listen); continue;
/* must be something wrong */
disconnect_all(state);
fprintf(stderr, "select failed: %s\n", strerror(errno));
exit(1);
}
else if (nsocks == 0)
{ /* timeout */
fprintf(stderr, "select timeout\n");
for (i = 0; i < nclients; i++)
{
fprintf(stderr, "client %d:state %d cnt %d ecnt %d listen %d\n",
i, state[i].state, state[i].cnt, state[i].ecnt, state[i].listen);
}
exit(0);
} }
exit(0);
} }
/* ok, backend returns reply */ /* ok, backend returns reply */
for (i = 0; i < nclients; i++) for (i = 0; i < nclients; i++)
{ {
if (state[i].con && FD_ISSET(PQsocket(state[i].con), &input_mask)) if (state[i].con && (FD_ISSET(PQsocket(state[i].con), &input_mask)
|| (ttype == 3
&& commands[state[i].state]->type == META_COMMAND)))
{ {
if (ttype == 0 || ttype == 2) if (ttype == 0 || ttype == 2)
doOne(state, i, debug, ttype); doOne(state, i, debug, ttype);
else if (ttype == 1) else if (ttype == 1)
doSelectOnly(state, i, debug); doSelectOnly(state, i, debug);
else if (ttype == 3)
doCustom(state, i, debug);
} }
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment