diff -cpr HEAD/contrib/pgbench/pgbench.c pgbench_querymode/contrib/pgbench/pgbench.c
*** HEAD/contrib/pgbench/pgbench.c	Wed Mar 12 11:18:33 2008
--- pgbench_querymode/contrib/pgbench/pgbench.c	Tue Mar 18 15:50:10 2008
*************** typedef struct
*** 103,108 ****
--- 103,110 ----
  	char	   *value;			/* its value */
  }	Variable;
  
+ #define MAX_FILES		128		/* max number of SQL script files allowed */
+ 
  /*
   * structures used in custom query mode
   */
*************** typedef struct
*** 122,127 ****
--- 124,130 ----
  	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
*** 131,136 ****
--- 134,150 ----
  #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_QUERYMODE
+ } 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
*** 138,145 ****
  	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 */
  
--- 152,157 ----
*************** static char *select_only = {
*** 187,193 ****
  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][-d][dbname]\n");
  	fprintf(stderr, "(initialize mode): pgbench -i [-h hostname][-p port][-s scaling_factor] [-F fillfactor] [-U login][-d][dbname]\n");
  }
  
--- 199,205 ----
  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][-d][dbname]\n");
  	fprintf(stderr, "(initialize mode): pgbench -i [-h hostname][-p port][-s scaling_factor] [-F fillfactor] [-U login][-d][dbname]\n");
  }
  
*************** putVariable(CState * st, char *name, cha
*** 398,464 ****
  }
  
  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((unsigned char) sql[i]) || sql[i] == '_');
! 		if (i == j + 1)
! 			continue;
  
! 		name = malloc(i - j);
  		if (name == NULL)
! 			return NULL;
! 		memcpy(name, &sql[j + 1], i - (j + 1));
! 		name[i - (j + 1)] = '\0';
  		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;
  }
  
  static void
--- 410,506 ----
  }
  
  static char *
+ parseVariable(const char *sql, int *eaten)
+ {
+ 	int		i = 0;
+ 	char   *name;
+ 
+ 	do
+ 	{
+ 		i++;
+ 	} while (isalnum((unsigned char) sql[i]) || sql[i] == '_');
+ 	if (i == 1)
+ 		return NULL;
+ 
+ 	name = malloc(i);
+ 	if (name == NULL)
+ 		return NULL;
+ 	memcpy(name, &sql[1], i - 1);
+ 	name[i - 1] = '\0';
+ 
+ 	*eaten = i;
+ 	return name;
+ }
+ 
+ static char *
+ replaceVariable(char **sql, char *param, int len, char *value)
+ {
+ 	int	valueln = strlen(value);
+ 
+ 	if (valueln > len)
+ 	{
+ 		char   *tmp;
+ 		size_t	offset = param - *sql;
+ 
+ 		tmp = realloc(*sql, strlen(*sql) - len + valueln + 1);
+ 		if (tmp == NULL)
+ 		{
+ 			free(*sql);
+ 			return NULL;
+ 		}
+ 		*sql = tmp;
+ 		param = *sql + offset;
+ 	}
+ 
+ 	if (valueln != len)
+ 		memmove(param + valueln, param + len, strlen(param + len) + 1);
+ 	strncpy(param, value, valueln);
+ 
+ 	return param + valueln;
+ }
+ 
+ static char *
  assignVariables(CState * st, char *sql)
  {
  	char	   *p,
  			   *name,
  			   *val;
  
! 	p = sql;
! 	while ((p = strchr(p, ':')) != NULL)
  	{
! 		int		eaten;
  
! 		name = parseVariable(p, &eaten);
  		if (name == NULL)
! 			continue;
! 
  		val = getVariable(st, name);
  		free(name);
  		if (val == NULL)
  			continue;
  
! 		if ((p = replaceVariable(&sql, p, eaten, val)) == NULL)
! 			return NULL;
! 	}
  
! 	return sql;
! }
  
! 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
*************** top:
*** 580,608 ****
  
  	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)
  	{
--- 622,704 ----
  
  	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)
*** 936,941 ****
--- 1032,1080 ----
  	PQfinish(con);
  }
  
+ /*
+  * Parse the raw sql and replace :param to $n.
+  */
+ static bool
+ parseQuery(Command *cmd, const char *raw_sql)
+ {
+ 	char	   *sql,
+ 			   *p,
+ 			   *name;
+ 
+ 	sql = strdup(raw_sql);
+ 	if (sql == NULL)
+ 		return false;
+ 	cmd->argc = 1;
+ 
+ 	p = sql;
+ 	while ((p = strchr(p, ':')) != NULL)
+ 	{
+ 		char	hvar[12];
+ 		int		eaten;
+ 
+ 		if (cmd->argc > MAX_ARGS)
+ 		{
+ 			fprintf(stderr, "too many args: %s\n", raw_sql);
+ 			return false;
+ 		}
+ 
+ 		name = parseVariable(p, &eaten);
+ 		if (name == NULL)
+ 			continue;
+ 
+ 		sprintf(var, "$%d", cmd->argc);
+ 		if ((p = replaceVariable(&sql, p, eaten, var)) == NULL)
+ 			return false;
+ 
+ 		cmd->argv[cmd->argc] = name;
+ 		cmd->argc++;
+ 	}
+ 
+ 	cmd->argv[0] = sql;
+ 	return true;
+ }
+ 
  static Command *
  process_commands(char *buf)
  {
*************** process_commands(char *buf)
*** 1042,1051 ****
  	{
  		my_commands->type = SQL_COMMAND;
  
! 		if ((my_commands->argv[0] = strdup(p)) == NULL)
! 			return NULL;
! 
! 		my_commands->argc++;
  	}
  
  	return my_commands;
--- 1181,1201 ----
  	{
  		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(
*** 1222,1227 ****
--- 1372,1378 ----
  
  	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)
*** 1289,1295 ****
  
  	memset(state, 0, sizeof(*state));
  
! 	while ((c = getopt(argc, argv, "ih:nvp:dc:t:s:U:CNSlf:D:F:")) != -1)
  	{
  		switch (c)
  		{
--- 1440,1446 ----
  
  	memset(state, 0, sizeof(*state));
  
! 	while ((c = getopt(argc, argv, "ih:nvp:dc:t:s:U:CNSlf:D:F:M:")) != -1)
  	{
  		switch (c)
  		{
*************** main(int argc, char **argv)
*** 1396,1401 ****
--- 1547,1562 ----
  				if ((fillfactor < 10) || (fillfactor > 100))
  				{
  					fprintf(stderr, "invalid fillfactor: %d\n", fillfactor);
+ 					exit(1);
+ 				}
+ 				break;
+ 			case 'M':
+ 				for (querymode = 0; querymode < NUM_QUERYMODE; querymode++)
+ 					if (strcmp(optarg, QUERYMODE[querymode]) == 0)
+ 						break;
+ 				if (querymode >= NUM_QUERYMODE)
+ 				{
+ 					fprintf(stderr, "invalid querymode: %s\n", optarg);
  					exit(1);
  				}
  				break;
diff -cpr HEAD/doc/src/sgml/pgbench.sgml pgbench_querymode/doc/src/sgml/pgbench.sgml
*** HEAD/doc/src/sgml/pgbench.sgml	Tue Dec 11 11:31:49 2007
--- pgbench_querymode/doc/src/sgml/pgbench.sgml	Tue Mar 18 15:50:10 2008
*************** pgbench <optional> <replaceable>options<
*** 174,179 ****
--- 174,196 ----
        </entry>
       </row>
       <row>
+       <entry><literal>-M</literal> <replaceable>querymode</></entry>
+       <entry>
+        Choose the query mode from the follows. default is simple.
+          <itemizedlist>
+           <listitem>
+            <para>simple: using simple query protocol.</para>
+           </listitem>
+           <listitem>
+            <para>extended: using extended protocol.</para>
+           </listitem>
+           <listitem>
+            <para>prepared: using extended protocol with prepared statements.</para>
+           </listitem>
+          </itemizedlist>
+       </entry>
+      </row>
+      <row>
        <entry><literal>-N</literal></entry>
        <entry>
         Do not update <structname>tellers</> and <structname>branches</>.
