From 0bab31f3f1dbddc3006b05c9d6cb3ca9d4ee1b54 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Thu, 7 Jan 2016 15:54:19 +0900
Subject: [PATCH 3/3] Change the way to hold command list.

Currently commands for SQL statements are generated as a linked list
and stored into and accessed as an array. This patch unifies the way
to store them to linked list.
---
 src/bin/pgbench/pgbench.c | 189 +++++++++++++++++++++-------------------------
 1 file changed, 85 insertions(+), 104 deletions(-)

diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 8793fd2..e934e87 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -191,6 +191,7 @@ typedef struct
 
 #define MAX_SCRIPTS		128		/* max number of SQL scripts allowed */
 #define SHELL_COMMAND_SIZE	256 /* maximum size allowed for shell command */
+#define MAX_ARGS		10
 
 /*
  * Simple data structure to keep stats about something.
@@ -222,13 +223,29 @@ typedef struct StatsData
 } StatsData;
 
 /*
+ * Structure for individual command
+ */
+typedef struct Command_t
+{
+	char	   *line;			/* full text of command line */
+	int			command_num;	/* unique index of this Command struct */
+	int			type;			/* command type (SQL_COMMAND or META_COMMAND) */
+	int			argc;			/* number of command words */
+	char	   *argv[MAX_ARGS]; /* command word list */
+	int			cols[MAX_ARGS]; /* corresponding column starting from 1 */
+	PgBenchExpr *expr;			/* parsed expression */
+	SimpleStats stats;			/* time spent in this command */
+	struct Command_t *next;		/* more command if any, for multistatements */
+} Command;
+
+/*
  * Connection state
  */
 typedef struct
 {
 	PGconn	   *con;			/* connection handle to DB */
 	int			id;				/* client No. */
-	int			state;			/* state No. */
+	Command	   *curr;			/* current command */
 	bool		listen;			/* whether an async query has been sent */
 	bool		is_throttled;	/* whether transaction throttling is done */
 	bool		sleeping;		/* whether the client is napping */
@@ -273,7 +290,6 @@ typedef struct
  */
 #define SQL_COMMAND		1
 #define META_COMMAND	2
-#define MAX_ARGS		10
 
 typedef enum QueryMode
 {
@@ -286,23 +302,10 @@ typedef enum QueryMode
 static QueryMode querymode = QUERY_SIMPLE;
 static const char *QUERYMODE[] = {"simple", "extended", "prepared"};
 
-typedef struct Command_t
-{
-	char	   *line;			/* full text of command line */
-	int			command_num;	/* unique index of this Command struct */
-	int			type;			/* command type (SQL_COMMAND or META_COMMAND) */
-	int			argc;			/* number of command words */
-	char	   *argv[MAX_ARGS]; /* command word list */
-	int			cols[MAX_ARGS]; /* corresponding column starting from 1 */
-	PgBenchExpr *expr;			/* parsed expression */
-	SimpleStats stats;			/* time spent in this command */
-	struct Command_t *next;		/* more command if any, for multistatements */
-} Command;
-
 static struct
 {
 	const char *name;
-	Command   **commands;
+	Command   *commands;
 	StatsData stats;
 }	sql_script[MAX_SCRIPTS];	/* SQL script files */
 
@@ -1271,7 +1274,7 @@ static bool
 doCustom(TState *thread, CState *st, StatsData *agg)
 {
 	PGresult   *res;
-	Command   **commands;
+	Command    *commands;
 	bool		trans_needs_throttle = false;
 	instr_time	now;
 
@@ -1351,13 +1354,13 @@ top:
 
 	if (st->listen)
 	{							/* are we receiver? */
-		if (commands[st->state]->type == SQL_COMMAND)
+		if (st->curr->type == SQL_COMMAND)
 		{
 			if (debug)
 				fprintf(stderr, "client %d receiving\n", st->id);
 			if (!PQconsumeInput(st->con))
 			{					/* there's something wrong */
-				fprintf(stderr, "client %d aborted in state %d; perhaps the backend died while processing\n", st->id, st->state);
+				fprintf(stderr, "client %d aborted in state %d; perhaps the backend died while processing\n", st->id, st->curr->command_num);
 				return clientDone(st, false);
 			}
 			if (PQisBusy(st->con))
@@ -1374,13 +1377,13 @@ top:
 				INSTR_TIME_SET_CURRENT(now);
 
 			/* XXX could use a mutex here, but we choose not to */
-			addToSimpleStats(&commands[st->state]->stats,
+			addToSimpleStats(&st->curr->stats,
 							 INSTR_TIME_GET_DOUBLE(now) -
 							 INSTR_TIME_GET_DOUBLE(st->stmt_begin));
 		}
 
 		/* transaction finished: calculate latency and log the transaction */
-		if (commands[st->state + 1] == NULL)
+		if (st->curr->next == NULL)
 		{
 			if (progress || throttle_delay || latency_limit ||
 				per_script_stats || use_log)
@@ -1389,7 +1392,7 @@ top:
 				thread->stats.cnt++;
 		}
 
-		if (commands[st->state]->type == SQL_COMMAND)
+		if (st->curr->type == SQL_COMMAND)
 		{
 			/*
 			 * Read and discard the query result; note this is not included in
@@ -1403,7 +1406,8 @@ top:
 					break;		/* OK */
 				default:
 					fprintf(stderr, "client %d aborted in state %d: %s",
-							st->id, st->state, PQerrorMessage(st->con));
+							st->id, st->curr->command_num,
+							PQerrorMessage(st->con));
 					PQclear(res);
 					return clientDone(st, false);
 			}
@@ -1411,7 +1415,7 @@ top:
 			discard_response(st);
 		}
 
-		if (commands[st->state + 1] == NULL)
+		if (st->curr->next == NULL)
 		{
 			if (is_connect)
 			{
@@ -1424,16 +1428,16 @@ top:
 				return clientDone(st, true);	/* exit success */
 		}
 
-		/* increment state counter */
-		st->state++;
-		if (commands[st->state] == NULL)
+		/* move to the next state */
+		st->curr = st->curr->next;
+		if (st->curr == NULL)
 		{
-			st->state = 0;
 			st->use_file = chooseScript(thread);
 			commands = sql_script[st->use_file].commands;
 			if (debug)
 				fprintf(stderr, "client %d executing script \"%s\"\n", st->id,
 						sql_script[st->use_file].name);
+			st->curr = commands;
 			st->is_throttled = false;
 
 			/*
@@ -1477,7 +1481,7 @@ top:
 
 	/* Record transaction start time under logging, progress or throttling */
 	if ((use_log || progress || throttle_delay || latency_limit ||
-		 per_script_stats) && st->state == 0)
+		 per_script_stats) && st->curr == commands)
 	{
 		INSTR_TIME_SET_CURRENT(st->txn_begin);
 
@@ -1493,9 +1497,9 @@ top:
 	if (is_latencies)
 		INSTR_TIME_SET_CURRENT(st->stmt_begin);
 
-	if (commands[st->state]->type == SQL_COMMAND)
+	if (st->curr->type == SQL_COMMAND)
 	{
-		const Command *command = commands[st->state];
+		const Command *command = st->curr;
 		int			r;
 
 		if (querymode == QUERY_SIMPLE)
@@ -1529,18 +1533,19 @@ top:
 
 			if (!st->prepared[st->use_file])
 			{
-				int			j;
+				int			j = 0;
+				Command		*pcom = commands;
 
-				for (j = 0; commands[j] != NULL; j++)
+				for (; pcom ; pcom = pcom->next, j++)
 				{
 					PGresult   *res;
 					char		name[MAX_PREPARE_NAME];
 
-					if (commands[j]->type != SQL_COMMAND)
+					if (pcom->type != SQL_COMMAND)
 						continue;
 					preparedStatementName(name, st->use_file, j);
 					res = PQprepare(st->con, name,
-						  commands[j]->argv[0], commands[j]->argc - 1, NULL);
+						  pcom->argv[0], pcom->argc - 1, NULL);
 					if (PQresultStatus(res) != PGRES_COMMAND_OK)
 						fprintf(stderr, "%s", PQerrorMessage(st->con));
 					PQclear(res);
@@ -1549,7 +1554,7 @@ top:
 			}
 
 			getQueryParams(st, command, params);
-			preparedStatementName(name, st->use_file, st->state);
+			preparedStatementName(name, st->use_file, st->curr->command_num);
 
 			if (debug)
 				fprintf(stderr, "client %d sending %s\n", st->id, name);
@@ -1569,11 +1574,11 @@ top:
 		else
 			st->listen = true;	/* flags that should be listened */
 	}
-	else if (commands[st->state]->type == META_COMMAND)
+	else if (st->curr->type == META_COMMAND)
 	{
-		int			argc = commands[st->state]->argc,
+		int			argc = st->curr->argc,
 					i;
-		char	  **argv = commands[st->state]->argv;
+		char	  **argv = st->curr->argv;
 
 		if (debug)
 		{
@@ -1723,7 +1728,7 @@ top:
 		else if (pg_strcasecmp(argv[0], "set") == 0)
 		{
 			char		res[64];
-			PgBenchExpr *expr = commands[st->state]->expr;
+			PgBenchExpr *expr = st->curr->expr;
 			int64		result;
 
 			if (!evaluateExpr(st, expr, &result))
@@ -2697,36 +2702,28 @@ read_line_from_file(FILE *fd)
  * Given a file name, read it and return the array of Commands contained
  * therein.  "-" means to read stdin.
  */
-static Command **
+static Command *
 process_file(char *filename)
 {
-#define COMMANDS_ALLOC_NUM 128
-
-	Command   **my_commands;
+	Command    *my_commands = NULL,
+			   *my_commands_tail = NULL;
 	FILE	   *fd;
-	int			lineno,
-				index;
+	int			lineno;
 	char	   *buf;
-	int			alloc_num;
 	ParseInfo proc_state = createParseInfo();
 
-	alloc_num = COMMANDS_ALLOC_NUM;
-	my_commands = (Command **) pg_malloc(sizeof(Command *) * alloc_num);
-
 	if (strcmp(filename, "-") == 0)
 		fd = stdin;
 	else if ((fd = fopen(filename, "r")) == NULL)
 	{
 		fprintf(stderr, "could not open file \"%s\": %s\n",
 				filename, strerror(errno));
-		pg_free(my_commands);
 		return NULL;
 	}
 
 	proc_state->mode = PS_IDLE;
 
 	lineno = 0;
-	index = 0;
 
 	while ((buf = read_line_from_file(fd)) != NULL)
 	{
@@ -2748,44 +2745,35 @@ process_file(char *filename)
  			continue;
 		}
 
-		while (command)
-		{
-			my_commands[index++] = command;
-			command = command->next;
-		}
-		
-		if (index > alloc_num)
-		{
-			alloc_num += COMMANDS_ALLOC_NUM;
-			my_commands = pg_realloc(my_commands, sizeof(Command *) * alloc_num);
-		}
+		/* Append new commands at the end of the list */
+		if (my_commands_tail)
+			my_commands_tail->next = command;
+		else
+			my_commands = my_commands_tail = command;
+
+		/* Seek to the tail of the list */
+		while (my_commands_tail->next)
+			my_commands_tail = my_commands_tail->next;
 	}
 	fclose(fd);
 
 	parse_finish_scan(proc_state);
 
-	my_commands[index] = NULL;
+	my_commands_tail->next = NULL;
 
 	return my_commands;
 }
 
-static Command **
+static Command *
 process_builtin(const char *tb, const char *source)
 {
-#define COMMANDS_ALLOC_NUM 128
-
-	Command   **my_commands;
-	int			lineno,
-				index;
+	Command    *my_commands = NULL,
+			   *my_commands_tail = NULL;
+	int			lineno;
 	char		buf[BUFSIZ];
-	int			alloc_num;
 	ParseInfo proc_state = createParseInfo();
 
-	alloc_num = COMMANDS_ALLOC_NUM;
-	my_commands = (Command **) pg_malloc(sizeof(Command *) * alloc_num);
-
 	lineno = 0;
-	index = 0;
 
 	for (;;)
 	{
@@ -2810,19 +2798,17 @@ process_builtin(const char *tb, const char *source)
 		if (command == NULL)
 			continue;
 
-		/* builtin doesn't need multistatements */
+		/* For simplisity, inhibit builtin from multistatements */
 		Assert(command->next == NULL);
-		my_commands[index] = command;
-		index++;
-
-		if (index >= alloc_num)
-		{
-			alloc_num += COMMANDS_ALLOC_NUM;
-			my_commands = pg_realloc(my_commands, sizeof(Command *) * alloc_num);
+		if (my_commands_tail)
+ 		{
+			my_commands_tail->next = command;
+			my_commands_tail = command;
 		}
+		else
+			my_commands = my_commands_tail = command;
 	}
 
-	my_commands[index] = NULL;
 	parse_finish_scan(proc_state);
 
 	return my_commands;
@@ -2860,10 +2846,9 @@ findBuiltin(const char *name, char **desc)
 }
 
 static void
-addScript(const char *name, Command **commands)
+addScript(const char *name, Command *commands)
 {
-	if (commands == NULL ||
-		commands[0] == NULL)
+	if (commands == NULL)
 	{
 		fprintf(stderr, "empty command list for script \"%s\"\n", name);
 		exit(1);
@@ -2986,17 +2971,16 @@ printResults(TState *threads, StatsData *total, instr_time total_time,
 			/* Report per-command latencies */
 			if (is_latencies)
 			{
-				Command   **commands;
+				Command   *command;
 
 				printf(" - statement latencies in milliseconds:\n");
 
-				for (commands = sql_script[i].commands;
-					 *commands != NULL;
-					 commands++)
+				for (command = sql_script[i].commands;
+					 command != NULL;
+					 command = command->next)
 					printf("   %11.3f  %s\n",
-						   1000.0 * (*commands)->stats.sum /
-						   (*commands)->stats.count,
-						   (*commands)->line);
+						   1000.0 * command->stats.sum / command->stats.count,
+						   command->line);
 			}
 		}
 	}
@@ -3797,20 +3781,19 @@ threadRun(void *arg)
 	{
 		CState	   *st = &state[i];
 		int			prev_ecnt = st->ecnt;
-		Command   **commands;
 
 		st->use_file = chooseScript(thread);
-		commands = sql_script[st->use_file].commands;
+		st->curr = sql_script[st->use_file].commands;
 		if (debug)
 			fprintf(stderr, "client %d executing script \"%s\"\n", st->id,
 					sql_script[st->use_file].name);
 		if (!doCustom(thread, st, &aggs))
 			remains--;			/* I've aborted */
 
-		if (st->ecnt > prev_ecnt && commands[st->state]->type == META_COMMAND)
+		if (st->ecnt > prev_ecnt && st->curr->type == META_COMMAND)
 		{
 			fprintf(stderr, "client %d aborted in state %d; execution of meta-command failed\n",
-					i, st->state);
+					i, st->curr->command_num);
 			remains--;			/* I've aborted */
 			PQfinish(st->con);
 			st->con = NULL;
@@ -3831,7 +3814,6 @@ threadRun(void *arg)
 		for (i = 0; i < nstate; i++)
 		{
 			CState	   *st = &state[i];
-			Command   **commands = sql_script[st->use_file].commands;
 			int			sock;
 
 			if (st->con == NULL)
@@ -3867,7 +3849,7 @@ threadRun(void *arg)
 						min_usec = this_usec;
 				}
 			}
-			else if (commands[st->state]->type == META_COMMAND)
+			else if (st->curr->type == META_COMMAND)
 			{
 				min_usec = 0;	/* the connection is ready to run */
 				break;
@@ -3937,7 +3919,6 @@ threadRun(void *arg)
 		for (i = 0; i < nstate; i++)
 		{
 			CState	   *st = &state[i];
-			Command   **commands = sql_script[st->use_file].commands;
 			int			prev_ecnt = st->ecnt;
 
 			if (st->con)
@@ -3950,17 +3931,17 @@ threadRun(void *arg)
 					goto done;
 				}
 				if (FD_ISSET(sock, &input_mask) ||
-					commands[st->state]->type == META_COMMAND)
+					st->curr->type == META_COMMAND)
 				{
 					if (!doCustom(thread, st, &aggs))
 						remains--;		/* I've aborted */
 				}
 			}
 
-			if (st->ecnt > prev_ecnt && commands[st->state]->type == META_COMMAND)
+			if (st->ecnt > prev_ecnt && st->curr->type == META_COMMAND)
 			{
 				fprintf(stderr, "client %d aborted in state %d; execution of meta-command failed\n",
-						i, st->state);
+						i, st->curr->command_num);
 				remains--;		/* I've aborted */
 				PQfinish(st->con);
 				st->con = NULL;
-- 
1.8.3.1

