diff -cpr head/contrib/pgbench/README.pgbench pgbench_querymode/contrib/pgbench/README.pgbench
*** head/contrib/pgbench/README.pgbench	Wed Oct 17 15:19:18 2007
--- pgbench_querymode/contrib/pgbench/README.pgbench	Wed Oct 17 15:20:08 2007
*************** o options
*** 163,168 ****
--- 163,176 ----
  		0 201 2513 0 1175850569 608
  		0 202 2038 0 1175850569 2663
  
+ 	-M querymode
+ 
+ 		Choose the query mode from the follows:
+ 		    simple: using simple query protocol.
+ 		    extended: using extended protocol.
+ 		    prepared: using extended protocol with prepared statements.
+ 		default is simple.
+ 
  	-F fillfactor
  
  		Create tables(accounts, tellers and branches) with the given
diff -cpr head/contrib/pgbench/pgbench.c pgbench_querymode/contrib/pgbench/pgbench.c
*** head/contrib/pgbench/pgbench.c	Wed Oct 17 15:19:18 2007
--- pgbench_querymode/contrib/pgbench/pgbench.c	Wed Oct 17 14:53:44 2007
*************** typedef struct
*** 102,107 ****
--- 102,109 ----
  	char	   *value;			/* its value */
  }	Variable;
  
+ #define MAX_FILES		128		/* max number of SQL script files allowed */
+ 
  /*
   * structures used in custom query mode
   */
*************** typedef struct
*** 121,126 ****
--- 123,129 ----
  	int			nvariables;
  	struct timeval txn_begin;	/* used for measuring latencies */
  	int			use_file;		/* index in sql_files for this client */
+ 	bool		prepared[MAX_FILES];
  }	CState;
  
  /*
*************** typedef struct
*** 130,135 ****
--- 133,149 ----
  #define META_COMMAND	2
  #define MAX_ARGS		10
  
+ typedef enum QueryMode
+ {
+ 	QUERY_SIMPLE,	/* simple query */
+ 	QUERY_EXTENDED,	/* extended query */
+ 	QUERY_PREPARED,	/* extended query with prepared statements */
+ 	NUM_SQLMODE
+ } QueryMode;
+ 
+ static QueryMode	querymode = QUERY_SIMPLE;
+ static const char *QUERYMODE[] = { "simple", "extended", "prepared" };
+ 
  typedef struct
  {
  	int			type;			/* command type (SQL_COMMAND or META_COMMAND) */
*************** typedef struct
*** 137,144 ****
  	char	   *argv[MAX_ARGS]; /* command list */
  }	Command;
  
- #define MAX_FILES		128		/* max number of SQL script files allowed */
- 
  Command   **sql_files[MAX_FILES];		/* SQL script files */
  int			num_files;			/* its number */
  
--- 151,156 ----
*************** static char *select_only = {
*** 186,192 ****
  static void
  usage(void)
  {
! 	fprintf(stderr, "usage: pgbench [-h hostname][-p port][-c nclients][-t ntransactions][-s scaling_factor][-D varname=value][-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] [-F fillfactor] [-U login][-P password][-d][dbname]\n");
  }
  
--- 198,204 ----
  static void
  usage(void)
  {
! 	fprintf(stderr, "usage: pgbench [-h hostname][-p port][-c nclients][-t ntransactions][-s scaling_factor][-D varname=value][-n][-C][-v][-S][-N][-f filename][-M querymode][-l][-U login][-P password][-d][dbname]\n");
  	fprintf(stderr, "(initialize mode): pgbench -i [-h hostname][-p port][-s scaling_factor] [-F fillfactor] [-U login][-P password][-d][dbname]\n");
  }
  
*************** assignVariables(CState * st, char *sql)
*** 439,444 ****
--- 451,472 ----
  }
  
  static void
+ getQueryParams(CState *st, const Command *command, const char **params)
+ {
+ 	int		i;
+ 
+ 	for (i = 0; i < command->argc - 1; i++)
+ 		params[i] = getVariable(st, command->argv[i+1]);
+ }
+ 
+ #define MAX_PREPARE_NAME		32
+ static void
+ preparedStatementName(char *buffer, int file, int state)
+ {
+ 	sprintf(buffer, "P%d_%d", file, state);
+ }
+ 
+ static void
  doCustom(CState * state, int n, int debug)
  {
  	PGresult   *res;
*************** top:
*** 557,585 ****
  
  	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 = 1;		/* flags that should be listened */
- 		}
- 		free(sql);
  	}
  	else if (commands[st->state]->type == META_COMMAND)
  	{
--- 585,667 ----
  
  	if (commands[st->state]->type == SQL_COMMAND)
  	{
! 		const Command  *command = commands[st->state];
! 		int				r;
  
! 		if (querymode == QUERY_SIMPLE)
  		{
! 			char	   *sql;
! 
! 			if ((sql = strdup(command->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);
! 			r = PQsendQuery(st->con, sql);
! 			free(sql);
  		}
+ 		else if (querymode == QUERY_EXTENDED)
+ 		{
+ 			const char		 *sql = command->argv[0];
+ 			const char		 *params[MAX_ARGS];
  
! 			getQueryParams(st, command, params);
! 
! 			if (debug)
! 				fprintf(stderr, "client %d sending %s\n", n, sql);
! 			r = PQsendQueryParams(st->con, sql, command->argc - 1,
! 				NULL, params, NULL, NULL, 0);
! 		}
! 		else if (querymode == QUERY_PREPARED)
  		{
+ 			char		name[MAX_PREPARE_NAME];
+ 			const char *params[MAX_ARGS];
+ 
+ 			if (!st->prepared[st->use_file])
+ 			{
+ 				int		j;
+ 
+ 				for (j = 0; commands[j] != NULL; j++)
+ 				{
+ 					PGresult   *res;
+ 					char		name[MAX_PREPARE_NAME];
+ 
+ 					if (commands[j]->type != SQL_COMMAND)
+ 						continue;
+ 					preparedStatementName(name, st->use_file, j);
+ 					res = PQprepare(st->con, name,
+ 						commands[j]->argv[0], commands[j]->argc - 1, NULL);
+ 					if (PQresultStatus(res) != PGRES_COMMAND_OK)
+ 						fprintf(stderr, "%s", PQerrorMessage(st->con));
+ 					PQclear(res);
+ 				}
+ 				st->prepared[st->use_file] = true;
+ 			}
+ 
+ 			getQueryParams(st, command, params);
+ 			preparedStatementName(name, st->use_file, st->state);
+ 
  			if (debug)
! 				fprintf(stderr, "client %d sending %s\n", n, name);
! 			r = PQsendQueryPrepared(st->con, name, command->argc - 1,
! 				params, NULL, NULL, 0);
! 		}
! 		else /* unknown sql mode */
! 			r = 0;
! 
! 		if (r == 0)
! 		{
! 			if (debug)
! 				fprintf(stderr, "client %d cannot send %s\n", n, command->argv[0]);
  			st->ecnt++;
  		}
  		else
  			st->listen = 1;		/* flags that should be listened */
  	}
  	else if (commands[st->state]->type == META_COMMAND)
  	{
*************** init(void)
*** 902,907 ****
--- 984,1046 ----
  	PQfinish(con);
  }
  
+ /*
+  * Parse the raw sql and replace :param to $n.
+  */
+ static bool
+ parseQuery(Command *cmd, const char *raw_sql)
+ {
+ 	int			i,
+ 				j;
+ 	char	   *sql,
+ 			   *p,
+ 			   *name;
+ 
+ 	sql = strdup(raw_sql);
+ 	if (sql == NULL)
+ 		return false;
+ 	cmd->argc = 1;
+ 
+ 	i = 0;
+ 	while ((p = strchr(&sql[i], ':')) != NULL)
+ 	{
+ 		char	param[12];
+ 		int		paramln;
+ 
+ 		if (cmd->argc > MAX_ARGS)
+ 		{
+ 			fprintf(stderr, "too many args: %s\n", raw_sql);
+ 			return false;
+ 		}
+ 
+ 		i = j = p - sql;
+ 		do
+ 		{
+ 			i++;
+ 		} while (isalnum((unsigned char) sql[i]) || sql[i] == '_');
+ 		if (i == j + 1)
+ 			continue;
+ 
+ 		name = malloc(i - j);
+ 		if (name == NULL)
+ 			return false;
+ 		memcpy(name, &sql[j + 1], i - (j + 1));
+ 		name[i - (j + 1)] = '\0';
+ 
+ 		paramln = sprintf(param, "$%d", cmd->argc);
+ 		if (paramln != i - j)
+ 			memmove(&sql[j + paramln], &sql[i], strlen(&sql[i]) + 1);
+ 		strncpy(&sql[j], param, paramln);
+ 		cmd->argv[cmd->argc] = name;
+ 		cmd->argc++;
+ 
+ 		i = j + paramln;
+ 	}
+ 
+ 	cmd->argv[0] = sql;
+ 	return true;
+ }
+ 
  static Command *
  process_commands(char *buf)
  {
*************** process_commands(char *buf)
*** 1008,1017 ****
  	{
  		my_commands->type = SQL_COMMAND;
  
! 		if ((my_commands->argv[0] = strdup(p)) == NULL)
! 			return NULL;
! 
! 		my_commands->argc++;
  	}
  
  	return my_commands;
--- 1147,1167 ----
  	{
  		my_commands->type = SQL_COMMAND;
  
! 		switch (querymode)
! 		{
! 			case QUERY_SIMPLE:
! 				if ((my_commands->argv[0] = strdup(p)) == NULL)
! 					return NULL;
! 				my_commands->argc++;
! 				break;
! 			case QUERY_EXTENDED:
! 			case QUERY_PREPARED:
! 				if (!parseQuery(my_commands, p))
! 					return NULL;
! 				break;
! 			default:
! 				return NULL;
! 		}
  	}
  
  	return my_commands;
*************** printResults(
*** 1188,1193 ****
--- 1338,1344 ----
  
  	printf("transaction type: %s\n", s);
  	printf("scaling factor: %d\n", scale);
+ 	printf("query mode: %s\n", QUERYMODE[querymode]);
  	printf("number of clients: %d\n", nclients);
  	printf("number of transactions per client: %d\n", nxacts);
  	printf("number of transactions actually processed: %d/%d\n", normal_xacts, nxacts * nclients);
*************** main(int argc, char **argv)
*** 1255,1261 ****
  
  	memset(state, 0, sizeof(*state));
  
! 	while ((c = getopt(argc, argv, "ih:nvp:dc:t:s:U:P:CNSlf:D:F:")) != -1)
  	{
  		switch (c)
  		{
--- 1406,1412 ----
  
  	memset(state, 0, sizeof(*state));
  
! 	while ((c = getopt(argc, argv, "ih:nvp:dc:t:s:U:P:CNSlf:D:F:M:")) != -1)
  	{
  		switch (c)
  		{
*************** main(int argc, char **argv)
*** 1365,1370 ****
--- 1516,1531 ----
  				if ((fillfactor < 10) || (fillfactor > 100))
  				{
  					fprintf(stderr, "invalid fillfactor: %d\n", fillfactor);
+ 					exit(1);
+ 				}
+ 				break;
+ 			case 'M':
+ 				for (querymode = 0; querymode < NUM_SQLMODE; querymode++)
+ 					if (strcmp(optarg, QUERYMODE[querymode]) == 0)
+ 						break;
+ 				if (querymode >= NUM_SQLMODE)
+ 				{
+ 					fprintf(stderr, "invalid querymode: %s\n", optarg);
  					exit(1);
  				}
  				break;
