From 6842b81ba19f779244e4cad3ab3ba69db537b3ea Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Thu, 23 Jul 2015 20:44:37 +0900
Subject: [PATCH 1/4] Prepare to share psqlscan with pgbench.

Eliminate direct usage of pset variables and enable parts unnecessary
for other than psql to be disabled by defining OUTSIDE_PSQL.
---
 src/bin/psql/mainloop.c |  6 ++--
 src/bin/psql/psqlscan.h | 14 +++++----
 src/bin/psql/psqlscan.l | 79 ++++++++++++++++++++++++++++++++-----------------
 src/bin/psql/startup.c  |  4 +--
 4 files changed, 67 insertions(+), 36 deletions(-)

diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c
index b6cef94..e98cb94 100644
--- a/src/bin/psql/mainloop.c
+++ b/src/bin/psql/mainloop.c
@@ -233,7 +233,8 @@ MainLoop(FILE *source)
 		/*
 		 * Parse line, looking for command separators.
 		 */
-		psql_scan_setup(scan_state, line, strlen(line));
+		psql_scan_setup(scan_state, line, strlen(line),
+						pset.db, pset.vars, pset.encoding);
 		success = true;
 		line_saved_in_history = false;
 
@@ -373,7 +374,8 @@ MainLoop(FILE *source)
 					resetPQExpBuffer(query_buf);
 					/* reset parsing state since we are rescanning whole line */
 					psql_scan_reset(scan_state);
-					psql_scan_setup(scan_state, line, strlen(line));
+					psql_scan_setup(scan_state, line, strlen(line),
+									pset.db, pset.vars, pset.encoding);
 					line_saved_in_history = false;
 					prompt_status = PROMPT_READY;
 				}
diff --git a/src/bin/psql/psqlscan.h b/src/bin/psql/psqlscan.h
index 55070ca..4bf8dcb 100644
--- a/src/bin/psql/psqlscan.h
+++ b/src/bin/psql/psqlscan.h
@@ -11,7 +11,11 @@
 #include "pqexpbuffer.h"
 
 #include "prompt.h"
-
+#if !defined OUTSIDE_PSQL
+#include "variables.h"
+#else
+typedef int * VariableSpace;
+#endif
 
 /* Abstract type for lexer's internal state */
 typedef struct PsqlScanStateData *PsqlScanState;
@@ -36,12 +40,11 @@ enum slash_option_type
 	OT_NO_EVAL					/* no expansion of backticks or variables */
 };
 
-
 extern PsqlScanState psql_scan_create(void);
 extern void psql_scan_destroy(PsqlScanState state);
 
-extern void psql_scan_setup(PsqlScanState state,
-				const char *line, int line_len);
+extern void psql_scan_setup(PsqlScanState state, const char *line, int line_len,
+							PGconn *db, VariableSpace vars, int encoding);
 extern void psql_scan_finish(PsqlScanState state);
 
 extern PsqlScanResult psql_scan(PsqlScanState state,
@@ -52,6 +55,7 @@ extern void psql_scan_reset(PsqlScanState state);
 
 extern bool psql_scan_in_quote(PsqlScanState state);
 
+#if !defined OUTSIDE_PSQL
 extern char *psql_scan_slash_command(PsqlScanState state);
 
 extern char *psql_scan_slash_option(PsqlScanState state,
@@ -60,5 +64,5 @@ extern char *psql_scan_slash_option(PsqlScanState state,
 					   bool semicolon);
 
 extern void psql_scan_slash_command_end(PsqlScanState state);
-
+#endif	 /* if !defined OUTSIDE_PSQL */
 #endif   /* PSQLSCAN_H */
diff --git a/src/bin/psql/psqlscan.l b/src/bin/psql/psqlscan.l
index be059ab..f9a19cd 100644
--- a/src/bin/psql/psqlscan.l
+++ b/src/bin/psql/psqlscan.l
@@ -43,11 +43,6 @@
 
 #include <ctype.h>
 
-#include "common.h"
-#include "settings.h"
-#include "variables.h"
-
-
 /*
  * We use a stack of flex buffers to handle substitution of psql variables.
  * Each stacked buffer contains the as-yet-unread text from one psql variable.
@@ -81,10 +76,12 @@ typedef struct PsqlScanStateData
 	const char *scanline;		/* current input line at outer level */
 
 	/* safe_encoding, curline, refline are used by emit() to replace FFs */
+	PGconn	   *db;				/* active connection */
 	int			encoding;		/* encoding being used now */
 	bool		safe_encoding;	/* is current encoding "safe"? */
 	const char *curline;		/* actual flex input string for cur buf */
 	const char *refline;		/* original data for cur buffer */
+	VariableSpace vars;			/* "shell variable" repository */
 
 	/*
 	 * All this state lives across successive input lines, until explicitly
@@ -126,6 +123,15 @@ static void escape_variable(bool as_ident);
 
 #define ECHO emit(yytext, yyleng)
 
+/* Provide dummy macros when no use of psql variables */
+#if defined OUTSIDE_PSQL
+#define GetVariable(space,name) NULL
+#define standard_strings() true
+#define psql_error(fmt,...) do { \
+	fprintf(stderr, "psql_error is called. abort.\n");\
+	exit(1);\
+} while(0)
+#endif
 %}
 
 %option 8bit
@@ -736,11 +742,14 @@ other			.
 
 :{variable_char}+	{
 					/* Possible psql variable substitution */
-					char   *varname;
-					const char *value;
+					char   *varname = NULL;
+					const char *value = NULL;
 
-					varname = extract_substring(yytext + 1, yyleng - 1);
-					value = GetVariable(pset.vars, varname);
+					if (cur_state->vars)
+					{
+						varname = extract_substring(yytext + 1, yyleng - 1);
+						value = GetVariable(cur_state->vars, varname);
+					}
 
 					if (value)
 					{
@@ -769,7 +778,8 @@ other			.
 						ECHO;
 					}
 
-					free(varname);
+					if (varname)
+						free(varname);
 				}
 
 :'{variable_char}+'	{
@@ -1033,9 +1043,12 @@ other			.
 						char   *varname;
 						const char *value;
 
-						varname = extract_substring(yytext + 1, yyleng - 1);
-						value = GetVariable(pset.vars, varname);
-						free(varname);
+						if (cur_state->vars)
+						{
+							varname = extract_substring(yytext + 1, yyleng - 1);
+							value = GetVariable(cur_state->vars, varname);
+							free(varname);
+						}
 
 						/*
 						 * The variable value is just emitted without any
@@ -1227,17 +1240,19 @@ psql_scan_destroy(PsqlScanState state)
  * or freed until after psql_scan_finish is called.
  */
 void
-psql_scan_setup(PsqlScanState state,
-				const char *line, int line_len)
+psql_scan_setup(PsqlScanState state, const char *line, int line_len,
+				PGconn *db, VariableSpace vars, int encoding)
 {
 	/* Mustn't be scanning already */
 	Assert(state->scanbufhandle == NULL);
 	Assert(state->buffer_stack == NULL);
 
 	/* Do we need to hack the character set encoding? */
-	state->encoding = pset.encoding;
+	state->encoding = encoding;
 	state->safe_encoding = pg_valid_server_encoding_id(state->encoding);
 
+	state->vars = vars;
+
 	/* needed for prepare_buffer */
 	cur_state = state;
 
@@ -1459,6 +1474,7 @@ psql_scan_in_quote(PsqlScanState state)
 	return state->start_state != INITIAL;
 }
 
+#if !defined OUTSIDE_PSQL
 /*
  * Scan the command name of a psql backslash command.  This should be called
  * after psql_scan() returns PSCAN_BACKSLASH.  It is assumed that the input
@@ -1615,7 +1631,7 @@ psql_scan_slash_option(PsqlScanState state,
 					{
 						if (!inquotes && type == OT_SQLID)
 							*cp = pg_tolower((unsigned char) *cp);
-						cp += PQmblen(cp, pset.encoding);
+						cp += PQmblen(cp, cur_state->encoding);
 					}
 				}
 			}
@@ -1744,6 +1760,14 @@ evaluate_backtick(void)
 
 	termPQExpBuffer(&cmd_output);
 }
+#else
+static void
+evaluate_backtick(void)
+{
+	fprintf(stderr, "Unexpected call of evaluate_backtick.\n");
+	exit(1);
+}
+#endif /* if !defined OUTSIDE_PSQL*/
 
 /*
  * Push the given string onto the stack of stuff to scan.
@@ -1944,15 +1968,18 @@ escape_variable(bool as_ident)
 	char	   *varname;
 	const char *value;
 
-	/* Variable lookup. */
-	varname = extract_substring(yytext + 2, yyleng - 3);
-	value = GetVariable(pset.vars, varname);
-	free(varname);
+	/* Variable lookup if possible. */
+	if (cur_state->vars && cur_state->db)
+	{
+		varname = extract_substring(yytext + 2, yyleng - 3);
+		value = GetVariable(cur_state->vars, varname);
+		free(varname);
+	}
 
 	/* Escaping. */
 	if (value)
 	{
-		if (!pset.db)
+		if (!cur_state->db)
 			psql_error("can't escape without active connection\n");
 		else
 		{
@@ -1960,16 +1987,14 @@ escape_variable(bool as_ident)
 
 			if (as_ident)
 				escaped_value =
-					PQescapeIdentifier(pset.db, value, strlen(value));
+					PQescapeIdentifier(cur_state->db, value, strlen(value));
 			else
 				escaped_value =
-					PQescapeLiteral(pset.db, value, strlen(value));
+					PQescapeLiteral(cur_state->db, value, strlen(value));
 
 			if (escaped_value == NULL)
 			{
-				const char *error = PQerrorMessage(pset.db);
-
-				psql_error("%s", error);
+				psql_error("%s", PQerrorMessage(cur_state->db));
 			}
 			else
 			{
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index 28ba75a..c143dfe 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -305,8 +305,8 @@ main(int argc, char *argv[])
 
 		scan_state = psql_scan_create();
 		psql_scan_setup(scan_state,
-						options.action_string,
-						strlen(options.action_string));
+						options.action_string, strlen(options.action_string),
+						pset.db, pset.vars, pset.encoding);
 
 		successResult = HandleSlashCmds(scan_state, NULL) != PSQL_CMD_ERROR
 			? EXIT_SUCCESS : EXIT_FAILURE;
-- 
1.8.3.1

