enhanced pgbench

Started by Tatsuo Ishiiover 20 years ago5 messages
#1Tatsuo Ishii
ishii@sraoss.co.jp
1 attachment(s)

Hi all,

We have enhanced pgbench so that it accepts a series of SQL commands
in a file(see attached patches against 8.0.3). This would make it
possible to test various sets of SQL commands. In the file it is
allowed to use a "meta command". Currently only "\setrandom" meta
command is allowed, which sets specified random number into a
variable. For example,

\setrandom aid 1 100000

will set a random number into variable "aid" between 1 and 10000.

A variable can be reffered to in an SQL command by adding ":" in front
of the the command name.

Here is an example SQL command file.

\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

This will execute virtually same SQL commands builtin pgbench.

To use the SQL command file, you can use "-f" option:

pgbench -f /foo/bar/sqlfile

I think the enhanced pgbench is quite usefull and I would like to
include in 8.1. Or should I keep it for 8.2?
--
SRA OSS, Inc. Japan
Tatsuo Ishii

Attachments:

pgbench.patchtext/plain; charset=us-asciiDownload
*** pgbench/pgbench.c	2004-11-09 15:09:31.000000000 +0900
--- pgbench-new/pgbench.c	2005-09-27 14:31:34.000000000 +0900
***************
*** 41,46 ****
--- 41,49 ----
  #include <sys/resource.h>
  #endif   /* ! WIN32 */
  
+ #include <ctype.h>
+ #include <search.h>
+ 
  extern char *optarg;
  extern int	optind;
  
***************
*** 72,77 ****
--- 75,83 ----
  #define ntellers	10
  #define naccounts	100000
  
+ #define SQL_COMMAND		1
+ #define META_COMMAND	2
+ 
  FILE	   *LOGFILE = NULL;
  
  bool		use_log;			/* log transaction latencies to a file */
***************
*** 91,96 ****
--- 97,108 ----
  
  typedef struct
  {
+ 	char	   *name;
+ 	char	   *value;
+ }	Variable;
+ 
+ typedef struct
+ {
  	PGconn	   *con;			/* connection handle to DB */
  	int			id;				/* client No. */
  	int			state;			/* state No. */
***************
*** 103,115 ****
  	int			tid;			/* teller id for this transaction */
  	int			delta;
  	int			abalance;
  	struct timeval txn_begin;	/* used for measuring latencies */
  }	CState;
  
  static 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, "(initialize mode): pgbench -i [-h hostname][-p port][-s scaling_factor][-U login][-P password][-d][dbname]\n");
  }
  
--- 115,137 ----
  	int			tid;			/* teller id for this transaction */
  	int			delta;
  	int			abalance;
+ 	void	   *variables;
  	struct timeval txn_begin;	/* used for measuring latencies */
  }	CState;
  
+ typedef struct
+ {
+ 	int			type;
+ 	int			argc;
+ 	char	  **argv;
+ }	Command;
+ 
+ Command	  **commands = NULL;
+ 
  static void
  usage(void)
  {
! 	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");
  }
  
***************
*** 190,195 ****
--- 212,326 ----
  	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 */
  static void
  doOne(CState * state, int n, int debug, int ttype)
***************
*** 465,470 ****
--- 596,765 ----
  	}
  }
  
+ 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 */
  static void
  disconnect_all(CState * state)
***************
*** 644,649 ****
--- 939,1098 ----
  	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 */
  static void
  printResults(
***************
*** 670,677 ****
  		s = "TPC-B (sort of)";
  	else if (ttype == 2)
  		s = "Update only accounts";
! 	else
  		s = "SELECT only";
  
  	printf("transaction type: %s\n", s);
  	printf("scaling factor: %d\n", tps);
--- 1119,1128 ----
  		s = "TPC-B (sort of)";
  	else if (ttype == 2)
  		s = "Update only accounts";
! 	else if (ttype == 1)
  		s = "SELECT only";
+ 	else
+ 		s = "Custom query";
  
  	printf("transaction type: %s\n", s);
  	printf("scaling factor: %d\n", tps);
***************
*** 695,700 ****
--- 1146,1152 ----
  	int			ttype = 0;		/* transaction type. 0: TPC-B, 1: SELECT
  								 * only, 2: skip update of branches and
  								 * tellers */
+ 	char	   *filename = NULL;
  
  	static CState *state;		/* status of clients */
  
***************
*** 724,730 ****
  	else if ((env = getenv("PGUSER")) != NULL && *env != '\0')
  		login = env;
  
! 	while ((c = getopt(argc, argv, "ih:nvp:dc:t:s:U:P:CNSl")) != -1)
  	{
  		switch (c)
  		{
--- 1176,1182 ----
  	else if ((env = getenv("PGUSER")) != NULL && *env != '\0')
  		login = env;
  
! 	while ((c = getopt(argc, argv, "ih:nvp:dc:t:s:U:P:CNSlf:")) != -1)
  	{
  		switch (c)
  		{
***************
*** 806,811 ****
--- 1258,1267 ----
  			case 'l':
  				use_log = true;
  				break;
+ 			case 'f':
+ 				ttype = 3;
+ 				filename = optarg;
+ 				break;
  			default:
  				usage();
  				exit(1);
***************
*** 868,941 ****
  		exit(1);
  	}
  
! 	/*
! 	 * 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));
! 		exit(1);
! 	}
! 	tps = atoi(PQgetvalue(res, 0, 0));
! 	if (tps < 0)
! 	{
! 		fprintf(stderr, "count(*) from branches invalid (%d)\n", tps);
! 		exit(1);
  	}
! 	PQclear(res);
! 
! 	if (!is_no_vacuum)
  	{
! 		fprintf(stderr, "starting vacuum...");
! 		res = PQexec(con, "vacuum branches");
! 		if (PQresultStatus(res) != PGRES_COMMAND_OK)
  		{
  			fprintf(stderr, "%s", PQerrorMessage(con));
  			exit(1);
  		}
! 		PQclear(res);
! 
! 		res = PQexec(con, "vacuum tellers");
! 		if (PQresultStatus(res) != PGRES_COMMAND_OK)
  		{
! 			fprintf(stderr, "%s", PQerrorMessage(con));
  			exit(1);
  		}
  		PQclear(res);
  
! 		res = PQexec(con, "delete from history");
! 		if (PQresultStatus(res) != PGRES_COMMAND_OK)
  		{
! 			fprintf(stderr, "%s", PQerrorMessage(con));
! 			exit(1);
! 		}
! 		PQclear(res);
! 		res = PQexec(con, "vacuum history");
! 		if (PQresultStatus(res) != PGRES_COMMAND_OK)
! 		{
! 			fprintf(stderr, "%s", PQerrorMessage(con));
! 			exit(1);
! 		}
! 		PQclear(res);
  
! 		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);
  
  	/* set random seed */
  	gettimeofday(&tv1, NULL);
--- 1324,1406 ----
  		exit(1);
  	}
  
! 	if (ttype == 3)
  	{
! 		PQfinish(con);
! 		if (process_file(filename) == false)
! 			exit(1);
  	}
! 	else
  	{
! 		/*
! 		 * 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));
  			exit(1);
  		}
! 		tps = atoi(PQgetvalue(res, 0, 0));
! 		if (tps < 0)
  		{
! 			fprintf(stderr, "count(*) from branches invalid (%d)\n", tps);
  			exit(1);
  		}
  		PQclear(res);
  
! 		if (!is_no_vacuum)
  		{
! 			fprintf(stderr, "starting vacuum...");
! 			res = PQexec(con, "vacuum branches");
! 			if (PQresultStatus(res) != PGRES_COMMAND_OK)
! 			{
! 				fprintf(stderr, "%s", PQerrorMessage(con));
! 				exit(1);
! 			}
! 			PQclear(res);
  
! 			res = PQexec(con, "vacuum tellers");
! 			if (PQresultStatus(res) != PGRES_COMMAND_OK)
! 			{
! 				fprintf(stderr, "%s", PQerrorMessage(con));
! 				exit(1);
! 			}
! 			PQclear(res);
  
! 			res = PQexec(con, "delete from history");
! 			if (PQresultStatus(res) != PGRES_COMMAND_OK)
! 			{
! 				fprintf(stderr, "%s", PQerrorMessage(con));
! 				exit(1);
! 			}
! 			PQclear(res);
! 			res = PQexec(con, "vacuum history");
  			if (PQresultStatus(res) != PGRES_COMMAND_OK)
  			{
  				fprintf(stderr, "%s", PQerrorMessage(con));
  				exit(1);
  			}
  			PQclear(res);
+ 
  			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);
  	}
  
  	/* set random seed */
  	gettimeofday(&tv1, NULL);
***************
*** 965,970 ****
--- 1430,1437 ----
  			doOne(state, i, debug, ttype);
  		else if (ttype == 1)
  			doSelectOnly(state, i, debug);
+ 		else if (ttype == 3)
+ 			doCustom(state, i, debug);
  	}
  
  	for (;;)
***************
*** 982,997 ****
  
  		FD_ZERO(&input_mask);
  
! 		maxsock = 0;
  		for (i = 0; i < nclients; i++)
  		{
! 			if (state[i].con)
  			{
  				int			sock = PQsocket(state[i].con);
  
  				if (sock < 0)
  				{
- 					fprintf(stderr, "Client %d: PQsocket failed\n", i);
  					disconnect_all(state);
  					exit(1);
  				}
--- 1449,1464 ----
  
  		FD_ZERO(&input_mask);
  
! 		maxsock = -1;
  		for (i = 0; i < nclients; i++)
  		{
! 			if (state[i].con &&
! 				(ttype != 3 || commands[state[i].state]->type != META_COMMAND))
  			{
  				int			sock = PQsocket(state[i].con);
  
  				if (sock < 0)
  				{
  					disconnect_all(state);
  					exit(1);
  				}
***************
*** 1001,1036 ****
  			}
  		}
  
! 		if ((nsocks = select(maxsock + 1, &input_mask, (fd_set *) NULL,
! 						  (fd_set *) NULL, (struct timeval *) NULL)) < 0)
  		{
! 			if (errno == EINTR)
! 				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);
  		}
  
  		/* ok, backend returns reply */
  		for (i = 0; i < nclients; i++)
  		{
! 			if (state[i].con && FD_ISSET(PQsocket(state[i].con), &input_mask))
  			{
  				if (ttype == 0 || ttype == 2)
  					doOne(state, i, debug, ttype);
  				else if (ttype == 1)
  					doSelectOnly(state, i, debug);
  			}
  		}
  	}
--- 1468,1510 ----
  			}
  		}
  
! 		if (maxsock != -1)
  		{
! 			if ((nsocks = select(maxsock + 1, &input_mask, (fd_set *) NULL,
! 							  (fd_set *) NULL, (struct timeval *) NULL)) < 0)
  			{
! 				if (errno == EINTR)
! 					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);
  			}
  		}
  
  		/* ok, backend returns reply */
  		for (i = 0; i < nclients; i++)
  		{
! 			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)
  					doOne(state, i, debug, ttype);
  				else if (ttype == 1)
  					doSelectOnly(state, i, debug);
+ 				else if (ttype == 3)
+ 					doCustom(state, i, debug);
  			}
  		}
  	}
#2Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tatsuo Ishii (#1)
Re: enhanced pgbench

Tatsuo Ishii <ishii@sraoss.co.jp> writes:

I think the enhanced pgbench is quite usefull and I would like to
include in 8.1. Or should I keep it for 8.2?

Well, we've traditionally been laxer about contrib than the core
software, so the fact that we're past feature freeze isn't sufficient
reason to say "no". I'm inclined to say "yes" because I think this
might make a handy debugging tool --- for instance, Janning Vygen's
current problem with temp tables might be reproducible using a pgbench
script, without having to write any new software.

Have you thought about ripping out all the original pgbench code and
instead having a "default script" that duplicates the original behavior?

Also it seems like it'd be useful if there could be more than one
script, so as to test situations where the clients aren't all doing
exactly the same thing.

regards, tom lane

#3Tatsuo Ishii
ishii@sraoss.co.jp
In reply to: Tom Lane (#2)
Re: enhanced pgbench

Tatsuo Ishii <ishii@sraoss.co.jp> writes:

I think the enhanced pgbench is quite usefull and I would like to
include in 8.1. Or should I keep it for 8.2?

Well, we've traditionally been laxer about contrib than the core
software, so the fact that we're past feature freeze isn't sufficient
reason to say "no". I'm inclined to say "yes" because I think this
might make a handy debugging tool --- for instance, Janning Vygen's
current problem with temp tables might be reproducible using a pgbench
script, without having to write any new software.

Ok. I will commit patches and will continue to work on below.

Have you thought about ripping out all the original pgbench code and
instead having a "default script" that duplicates the original behavior?

No, but seems an idea. There are few things I need to think about:

1) There are three pgbench's default behaviors: TPC-B like(default),
-N and -S. So we need three scripts.

2) it requires a location where those scripts reside. $prefix/etc
seems appropriate?

Also it seems like it'd be useful if there could be more than one
script, so as to test situations where the clients aren't all doing
exactly the same thing.

How do you assign different scripts to different clients?
--
SRA OSS, Inc. Japan
Tatsuo Ishii

#4Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tatsuo Ishii (#3)
Re: enhanced pgbench

Tatsuo Ishii <ishii@sraoss.co.jp> writes:

2) it requires a location where those scripts reside. $prefix/etc
seems appropriate?

I was thinking of just embedding the default scripts as constants in
the program text. A little bit ugly but saves a lot of headache with
needing to find installed files.

Also it seems like it'd be useful if there could be more than one
script, so as to test situations where the clients aren't all doing
exactly the same thing.

How do you assign different scripts to different clients?

I'd be happy with either round-robin or random selection of a new script
each time a thread finishes a script.

regards, tom lane

#5Tatsuo Ishii
ishii@sraoss.co.jp
In reply to: Tom Lane (#4)
Re: enhanced pgbench

Tatsuo Ishii <ishii@sraoss.co.jp> writes:

2) it requires a location where those scripts reside. $prefix/etc
seems appropriate?

I was thinking of just embedding the default scripts as constants in
the program text. A little bit ugly but saves a lot of headache with
needing to find installed files.

I see.

Also it seems like it'd be useful if there could be more than one
script, so as to test situations where the clients aren't all doing
exactly the same thing.

How do you assign different scripts to different clients?

I'd be happy with either round-robin or random selection of a new script
each time a thread finishes a script.

Ok.
--
SRA OSS, Inc. Japan
Tatsuo Ishii