--- psqlscan.l	2016-02-18 16:50:19.140495106 +0900
+++ psqlscanbody.l	2016-02-18 16:48:23.263579135 +0900
@@ -38,93 +38,22 @@
  *-------------------------------------------------------------------------
  */
-#include "postgres_fe.h"
-
 #include "psqlscan.h"
+#include "psqlscan_int.h"
 
 #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.
- * When we pop the stack all the way, we resume reading from the outer buffer
- * identified by scanbufhandle.
- */
-typedef struct StackElem
-{
-	YY_BUFFER_STATE buf;		/* flex input control structure */
-	char	   *bufstring;		/* data actually being scanned by flex */
-	char	   *origstring;		/* copy of original data, if needed */
-	char	   *varname;		/* name of variable providing data, or NULL */
-	struct StackElem *next;
-} StackElem;
-
-/*
- * All working state of the lexer must be stored in PsqlScanStateData
- * between calls.  This allows us to have multiple open lexer operations,
- * which is needed for nested include files.  The lexer itself is not
- * recursive, but it must be re-entrant.
- */
-typedef struct PsqlScanStateData
-{
-	StackElem  *buffer_stack;	/* stack of variable expansion buffers */
-	/*
-	 * These variables always refer to the outer buffer, never to any
-	 * stacked variable-expansion buffer.
-	 */
-	YY_BUFFER_STATE scanbufhandle;
-	char	   *scanbuf;		/* start of outer-level input buffer */
-	const char *scanline;		/* current input line at outer level */
-
-	/* safe_encoding, curline, refline are used by emit() to replace FFs */
-	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 */
-
-	/*
-	 * All this state lives across successive input lines, until explicitly
-	 * reset by psql_scan_reset.
-	 */
-	int			start_state;	/* saved YY_START */
-	int			paren_depth;	/* depth of nesting in parentheses */
-	int			xcdepth;		/* depth of nesting in slash-star comments */
-	char	   *dolqstart;		/* current $foo$ quote start string */
-} PsqlScanStateData;
-
 static PsqlScanState cur_state;	/* current state while active */
 
-static PQExpBuffer output_buf;	/* current output buffer */
+PQExpBuffer output_buf;	/* current output buffer */
+
+#define ECHO scan_emit(yytext, yyleng)
 
-/* these variables do not need to be saved across calls */
-static enum slash_option_type option_type;
-static char *option_quote;
-static int	unquoted_option_chars;
-static int	backtick_start_offset;
-
-
-/* Return values from yylex() */
-#define LEXRES_EOL			0	/* end of input */
-#define LEXRES_SEMI			1	/* command-terminating semicolon found */
-#define LEXRES_BACKSLASH	2	/* backslash command start */
-#define LEXRES_OK			3	/* OK completion of backslash argument */
-
-
-static void evaluate_backtick(void);
-static void push_new_buffer(const char *newstr, const char *varname);
-static void pop_buffer_stack(PsqlScanState state);
-static bool var_is_current_source(PsqlScanState state, const char *varname);
-static YY_BUFFER_STATE prepare_buffer(const char *txt, int len,
-									  char **txtcopy);
-static void emit(const char *txt, int len);
-static char *extract_substring(const char *txt, int len);
-static void escape_variable(bool as_ident);
+/* Adjust curpos on yyless */
+#define my_yyless(n) cur_state->curpos -= (yyleng - (n)); yyless(n)
 
-#define ECHO emit(yytext, yyleng)
+/* Track where lexer parsed up to */
+#define YY_USER_ACTION cur_state->curpos += yyleng;
 
+#define ENC_IS_SAFE(s) (!(s)->cb.enc_mblen)
 %}
 
@@ -186,13 +115,4 @@
 %x xus
 %x xusend
-/* Additional exclusive states for psql only: lex backslash commands */
-%x xslashcmd
-%x xslashargstart
-%x xslasharg
-%x xslashquote
-%x xslashbackquote
-%x xslashdquote
-%x xslashwholeline
-%x xslashend
 
 /*
@@ -434,5 +354,5 @@
 					BEGIN(xc);
 					/* Put back any characters past slash-star; see above */
-					yyless(2);
+					my_yyless(2);
 					ECHO;
 				}
@@ -441,5 +361,5 @@
 					cur_state->xcdepth++;
 					/* Put back any characters past slash-star; see above */
-					yyless(2);
+					my_yyless(2);
 					ECHO;
 				}
@@ -473,5 +393,5 @@
 <xb>{quotestop}	|
 <xb>{quotefail} {
-					yyless(1);
+					my_yyless(1);
 					BEGIN(INITIAL);
 					ECHO;
@@ -498,5 +418,5 @@
 <xh>{quotestop}	|
 <xh>{quotefail} {
-					yyless(1);
+					my_yyless(1);
 					BEGIN(INITIAL);
 					ECHO;
@@ -504,10 +424,10 @@
 
 {xnstart}		{
-					yyless(1);				/* eat only 'n' this time */
+					my_yyless(1);				/* eat only 'n' this time */
 					ECHO;
 				}
 
 {xqstart}		{
-					if (standard_strings())
+					if (cur_state->cb.standard_strings())
 						BEGIN(xq);
 					else
@@ -525,5 +445,5 @@
 <xq,xe>{quotestop}	|
 <xq,xe>{quotefail} {
-					yyless(1);
+					my_yyless(1);
 					BEGIN(INITIAL);
 					ECHO;
@@ -531,5 +451,5 @@
 <xus>{quotestop} |
 <xus>{quotefail} {
-					yyless(1);
+					my_yyless(1);
 					BEGIN(xusend);
 					ECHO;
@@ -540,5 +460,5 @@
 <xusend>{other} |
 <xusend>{xustop1} {
-					yyless(0);
+					my_yyless(0);
 					BEGIN(INITIAL);
 					ECHO;
@@ -587,5 +507,5 @@
 {dolqfailed}	{
 					/* throw back all but the initial "$" */
-					yyless(1);
+					my_yyless(1);
 					ECHO;
 				}
@@ -604,5 +524,5 @@
 						 * $ for rescanning.  Consider $delim$...$junk$delim$
 						 */
-						yyless(yyleng-1);
+						my_yyless(yyleng-1);
 					}
 					ECHO;
@@ -632,5 +552,5 @@
 				}
 <xui>{dquote} {
-					yyless(1);
+					my_yyless(1);
 					BEGIN(xuiend);
 					ECHO;
@@ -641,5 +561,5 @@
 <xuiend>{other} |
 <xuiend>{xustop1} {
-					yyless(0);
+					my_yyless(0);
 					BEGIN(INITIAL);
 					ECHO;
@@ -658,5 +578,5 @@
 {xufailed}	{
 					/* throw back all but the initial u/U */
-					yyless(1);
+					my_yyless(1);
 					ECHO;
 				}
@@ -727,5 +647,5 @@
 "\\"[;:]		{
 					/* Force a semicolon or colon into the query buffer */
-					emit(yytext + 1, 1);
+					scan_emit(yytext + 1, 1);
 				}
 
@@ -737,9 +657,14 @@
 :{variable_char}+	{
 					/* Possible psql variable substitution */
-					char   *varname;
-					const char *value;
+					char	   *varname = NULL;
+					const char *value = NULL;
+					void	  (*free_fn)(void *) = NULL;
 
-					varname = extract_substring(yytext + 1, yyleng - 1);
-					value = GetVariable(pset.vars, varname);
+					if (cur_state->cb.get_variable)
+					{
+						varname = extract_substring(yytext + 1, yyleng - 1);
+						value = cur_state->cb.get_variable(varname,
+ 									false, false, &free_fn);
+					}
 
 					if (value)
@@ -749,5 +674,5 @@
 						{
 							/* Recursive expansion --- don't go there */
-							psql_error("skipping recursive expansion of variable \"%s\"\n",
+							cur_state->cb.error_out("skipping recursive expansion of variable \"%s\"\n",
 									   varname);
 							/* Instead copy the string as is */
@@ -760,4 +685,6 @@
 							/* yy_scan_string already made buffer active */
 						}
+						if (free_fn)
+							free_fn((void*)value);
 					}
 					else
@@ -770,5 +697,6 @@
 					}
 
-					free(varname);
+					if (varname)
+						free(varname);
 				}
 
@@ -788,5 +716,5 @@
 :'{variable_char}*	{
 					/* Throw back everything but the colon */
-					yyless(1);
+					my_yyless(1);
 					ECHO;
 				}
@@ -794,5 +722,5 @@
 :\"{variable_char}*	{
 					/* Throw back everything but the colon */
-					yyless(1);
+					my_yyless(1);
 					ECHO;
 				}
@@ -855,5 +783,5 @@
 					{
 						/* Strip the unwanted chars from the token */
-						yyless(nchars);
+						my_yyless(nchars);
 					}
 					ECHO;
@@ -872,5 +800,5 @@
 {decimalfail}	{
 					/* throw back the .., and treat as integer */
-					yyless(yyleng-2);
+					my_yyless(yyleng-2);
 					ECHO;
 				}
@@ -885,10 +813,10 @@
 					 * syntax error anyway, we don't bother to distinguish.
 					 */
-					yyless(yyleng-1);
+					my_yyless(yyleng-1);
 					ECHO;
 				}
 {realfail2}		{
 					/* throw back the [Ee][+-], and proceed as above */
-					yyless(yyleng-2);
+					my_yyless(yyleng-2);
 					ECHO;
 				}
@@ -934,260 +862,24 @@
 					}
 				}
+%%
 
-	/*
-	 * Exclusive lexer states to handle backslash command lexing
-	 */
-
-<xslashcmd>{
-	/* command name ends at whitespace or backslash; eat all else */
-
-{space}|"\\"	{
-					yyless(0);
-					return LEXRES_OK;
-				}
-
-{other}			{ ECHO; }
-
-}
-
-<xslashargstart>{
-	/*
-	 * Discard any whitespace before argument, then go to xslasharg state.
-	 * An exception is that "|" is only special at start of argument, so we
-	 * check for it here.
-	 */
-
-{space}+		{ }
-
-"|"				{
-					if (option_type == OT_FILEPIPE)
-					{
-						/* treat like whole-string case */
-						ECHO;
-						BEGIN(xslashwholeline);
-					}
-					else
-					{
-						/* vertical bar is not special otherwise */
-						yyless(0);
-						BEGIN(xslasharg);
-					}
-				}
-
-{other}			{
-					yyless(0);
-					BEGIN(xslasharg);
-				}
-
-}
-
-<xslasharg>{
-	/*
-	 * Default processing of text in a slash command's argument.
-	 *
-	 * Note: unquoted_option_chars counts the number of characters at the
-	 * end of the argument that were not subject to any form of quoting.
-	 * psql_scan_slash_option needs this to strip trailing semicolons safely.
-	 */
-
-{space}|"\\"	{
-					/*
-					 * Unquoted space is end of arg; do not eat.  Likewise
-					 * backslash is end of command or next command, do not eat
-					 *
-					 * XXX this means we can't conveniently accept options
-					 * that include unquoted backslashes; therefore, option
-					 * processing that encourages use of backslashes is rather
-					 * broken.
-					 */
-					yyless(0);
-					return LEXRES_OK;
-				}
-
-{quote}			{
-					*option_quote = '\'';
-					unquoted_option_chars = 0;
-					BEGIN(xslashquote);
-				}
-
-"`"				{
-					backtick_start_offset = output_buf->len;
-					*option_quote = '`';
-					unquoted_option_chars = 0;
-					BEGIN(xslashbackquote);
-				}
-
-{dquote}		{
-					ECHO;
-					*option_quote = '"';
-					unquoted_option_chars = 0;
-					BEGIN(xslashdquote);
-				}
-
-:{variable_char}+	{
-					/* Possible psql variable substitution */
-					if (option_type == OT_NO_EVAL)
-						ECHO;
-					else
-					{
-						char   *varname;
-						const char *value;
-
-						varname = extract_substring(yytext + 1, yyleng - 1);
-						value = GetVariable(pset.vars, varname);
-						free(varname);
-
-						/*
-						 * The variable value is just emitted without any
-						 * further examination.  This is consistent with the
-						 * pre-8.0 code behavior, if not with the way that
-						 * variables are handled outside backslash commands.
-						 * Note that we needn't guard against recursion here.
-						 */
-						if (value)
-							appendPQExpBufferStr(output_buf, value);
-						else
-							ECHO;
-
-						*option_quote = ':';
-					}
-					unquoted_option_chars = 0;
-				}
-
-:'{variable_char}+'	{
-					if (option_type == OT_NO_EVAL)
-						ECHO;
-					else
-					{
-						escape_variable(false);
-						*option_quote = ':';
-					}
-					unquoted_option_chars = 0;
-				}
-
-
-:\"{variable_char}+\"	{
-					if (option_type == OT_NO_EVAL)
-						ECHO;
-					else
-					{
-						escape_variable(true);
-						*option_quote = ':';
-					}
-					unquoted_option_chars = 0;
-				}
-
-:'{variable_char}*	{
-					/* Throw back everything but the colon */
-					yyless(1);
-					unquoted_option_chars++;
-					ECHO;
-				}
-
-:\"{variable_char}*	{
-					/* Throw back everything but the colon */
-					yyless(1);
-					unquoted_option_chars++;
-					ECHO;
-				}
-
-{other}			{
-					unquoted_option_chars++;
-					ECHO;
-				}
-
-}
-
-<xslashquote>{
-	/*
-	 * single-quoted text: copy literally except for '' and backslash
-	 * sequences
-	 */
-
-{quote}			{ BEGIN(xslasharg); }
-
-{xqdouble}		{ appendPQExpBufferChar(output_buf, '\''); }
-
-"\\n"			{ appendPQExpBufferChar(output_buf, '\n'); }
-"\\t"			{ appendPQExpBufferChar(output_buf, '\t'); }
-"\\b"			{ appendPQExpBufferChar(output_buf, '\b'); }
-"\\r"			{ appendPQExpBufferChar(output_buf, '\r'); }
-"\\f"			{ appendPQExpBufferChar(output_buf, '\f'); }
-
-{xeoctesc}		{
-					/* octal case */
-					appendPQExpBufferChar(output_buf,
-										  (char) strtol(yytext + 1, NULL, 8));
-				}
-
-{xehexesc}		{
-					/* hex case */
-					appendPQExpBufferChar(output_buf,
-										  (char) strtol(yytext + 2, NULL, 16));
-				}
-
-"\\".			{ emit(yytext + 1, 1); }
-
-{other}|\n		{ ECHO; }
-
-}
-
-<xslashbackquote>{
-	/*
-	 * backticked text: copy everything until next backquote, then evaluate.
-	 *
-	 * XXX Possible future behavioral change: substitute for :VARIABLE?
-	 */
-
-"`"				{
-					/* In NO_EVAL mode, don't evaluate the command */
-					if (option_type != OT_NO_EVAL)
-						evaluate_backtick();
-					BEGIN(xslasharg);
-				}
-
-{other}|\n		{ ECHO; }
-
-}
-
-<xslashdquote>{
-	/* double-quoted text: copy verbatim, including the double quotes */
-
-{dquote}		{
-					ECHO;
-					BEGIN(xslasharg);
-				}
-
-{other}|\n		{ ECHO; }
-
-}
-
-<xslashwholeline>{
-	/* copy everything until end of input line */
-	/* but suppress leading whitespace */
-
-{space}+		{
-					if (output_buf->len > 0)
-						ECHO;
-				}
-
-{other}			{ ECHO; }
-
-}
-
-<xslashend>{
-	/* at end of command, eat a double backslash, but not anything else */
-
-"\\\\"			{ return LEXRES_OK; }
-
-{other}|\n		{
-					yyless(0);
-					return LEXRES_OK;
-				}
+static void my_psql_scan_finish(PsqlScanState state);
+static void my_psql_scan_reset(PsqlScanState state);
+static void psql_error_errout(const char *fmt, ...)
+	__attribute__ ((format (printf, 1, 2)));
+static bool psql_standard_strings(void);
 
+static void
+psql_scan_initialize(PsqlScanState state)
+{
+	psql_scan_finish(state);
+	psql_scan_reset(state);
+	memset(state, 0, sizeof(*state));
+	state->finish = &my_psql_scan_finish;
+	state->reset = &my_psql_scan_reset;
+	state->my_yy_scan_buffer = &yy_scan_buffer;
+	state->reset(state);
 }
 
-%%
-
 /*
  * Create a lexer working state struct.
@@ -1199,6 +891,5 @@
 
 	state = (PsqlScanStateData *) pg_malloc0(sizeof(PsqlScanStateData));
-
-	psql_scan_reset(state);
+	psql_scan_initialize(state);
 
 	return state;
@@ -1228,6 +919,6 @@
  */
 void
-psql_scan_setup(PsqlScanState state,
-				const char *line, int line_len)
+psql_scan_setup(PsqlScanState state, const char *line, int line_len,
+				PsqlScanCallbacks *cb)
 {
 	/* Mustn't be scanning already */
@@ -1235,7 +926,17 @@
 	Assert(state->buffer_stack == NULL);
 
-	/* Do we need to hack the character set encoding? */
-	state->encoding = pset.encoding;
-	state->safe_encoding = pg_valid_server_encoding_id(state->encoding);
+	/* copy callback functions */
+	state->cb.get_variable = cb->get_variable;
+	if (cb->standard_strings)
+		state->cb.standard_strings = cb->standard_strings;
+	else
+		state->cb.standard_strings = &psql_standard_strings;
+
+	state->cb.enc_mblen = cb->enc_mblen;
+
+	if (cb->error_out)
+		state->cb.error_out = cb->error_out;
+	else
+		state->cb.error_out = &psql_error_errout;
 
 	/* needed for prepare_buffer */
@@ -1246,4 +947,5 @@
 										  &state->scanbuf);
 	state->scanline = line;
+	state->curpos = 0;
 
 	/* Set lookaside data in case we have to map unsafe encoding */
@@ -1253,4 +955,23 @@
 
 /*
+ * Redirect functions for indirect calls. These functions may be called for
+ * scan state of other lexers.
+ */
+void
+psql_scan_finish(PsqlScanState state)
+{
+	if (state->finish)
+		state->finish(state);
+}
+
+void
+psql_scan_reset(PsqlScanState state)
+{
+	if (state->reset)
+		state->reset(state);
+}
+
+
+/*
  * Do lexical analysis of SQL command text.
  *
@@ -1409,6 +1130,6 @@
  * to deal with error recovery).
  */
-void
-psql_scan_finish(PsqlScanState state)
+static void
+my_psql_scan_finish(PsqlScanState state)
 {
 	/* Drop any incomplete variable expansions. */
@@ -1426,4 +1147,22 @@
 
 /*
+ * Create new lexer scanning state for this lexer which parses from the current
+ * position of the given scanning state for another lexer. The given state is
+ * destroyed.
+ * 
+ * Note: This function cannot access yy* functions and varialbes of the given
+ * state because they are of different lexer.
+ */
+void
+psql_scan_switch_lexer(PsqlScanState state)
+{
+	const char	   *newscanline = state->scanline + state->curpos;
+	PsqlScanCallbacks cb = state->cb;
+
+	psql_scan_initialize(state);
+	psql_scan_setup(state, newscanline, strlen(newscanline), &cb);
+}
+
+/*
  * Reset lexer scanning state to start conditions.  This is appropriate
  * for executing \r psql commands (or any other time that we discard the
@@ -1436,6 +1175,6 @@
  * done by psql_scan_finish().
  */
-void
-psql_scan_reset(PsqlScanState state)
+static void
+my_psql_scan_reset(PsqlScanState state)
 {
 	state->start_state = INITIAL;
@@ -1461,290 +1200,4 @@
 
 /*
- * 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
- * has been consumed through the leading backslash.
- *
- * The return value is a malloc'd copy of the command name, as parsed off
- * from the input.
- */
-char *
-psql_scan_slash_command(PsqlScanState state)
-{
-	PQExpBufferData mybuf;
-
-	/* Must be scanning already */
-	Assert(state->scanbufhandle != NULL);
-
-	/* Build a local buffer that we'll return the data of */
-	initPQExpBuffer(&mybuf);
-
-	/* Set up static variables that will be used by yylex */
-	cur_state = state;
-	output_buf = &mybuf;
-
-	if (state->buffer_stack != NULL)
-		yy_switch_to_buffer(state->buffer_stack->buf);
-	else
-		yy_switch_to_buffer(state->scanbufhandle);
-
-	BEGIN(xslashcmd);
-
-	/* And lex. */
-	yylex();
-
-	/* There are no possible errors in this lex state... */
-
-	return mybuf.data;
-}
-
-/*
- * Parse off the next argument for a backslash command, and return it as a
- * malloc'd string.  If there are no more arguments, returns NULL.
- *
- * type tells what processing, if any, to perform on the option string;
- * for example, if it's a SQL identifier, we want to downcase any unquoted
- * letters.
- *
- * if quote is not NULL, *quote is set to 0 if no quoting was found, else
- * the last quote symbol used in the argument.
- *
- * if semicolon is true, unquoted trailing semicolon(s) that would otherwise
- * be taken as part of the option string will be stripped.
- *
- * NOTE: the only possible syntax errors for backslash options are unmatched
- * quotes, which are detected when we run out of input.  Therefore, on a
- * syntax error we just throw away the string and return NULL; there is no
- * need to worry about flushing remaining input.
- */
-char *
-psql_scan_slash_option(PsqlScanState state,
-					   enum slash_option_type type,
-					   char *quote,
-					   bool semicolon)
-{
-	PQExpBufferData mybuf;
-	int			lexresult PG_USED_FOR_ASSERTS_ONLY;
-	char		local_quote;
-
-	/* Must be scanning already */
-	Assert(state->scanbufhandle != NULL);
-
-	if (quote == NULL)
-		quote = &local_quote;
-	*quote = 0;
-
-	/* Build a local buffer that we'll return the data of */
-	initPQExpBuffer(&mybuf);
-
-	/* Set up static variables that will be used by yylex */
-	cur_state = state;
-	output_buf = &mybuf;
-	option_type = type;
-	option_quote = quote;
-	unquoted_option_chars = 0;
-
-	if (state->buffer_stack != NULL)
-		yy_switch_to_buffer(state->buffer_stack->buf);
-	else
-		yy_switch_to_buffer(state->scanbufhandle);
-
-	if (type == OT_WHOLE_LINE)
-		BEGIN(xslashwholeline);
-	else
-		BEGIN(xslashargstart);
-
-	/* And lex. */
-	lexresult = yylex();
-
-	/*
-	 * Check the lex result: we should have gotten back either LEXRES_OK
-	 * or LEXRES_EOL (the latter indicating end of string).  If we were inside
-	 * a quoted string, as indicated by YY_START, EOL is an error.
-	 */
-	Assert(lexresult == LEXRES_EOL || lexresult == LEXRES_OK);
-
-	switch (YY_START)
-	{
-		case xslashargstart:
-			/* empty arg */
-			break;
-		case xslasharg:
-			/* Strip any unquoted trailing semi-colons if requested */
-			if (semicolon)
-			{
-				while (unquoted_option_chars-- > 0 &&
-					   mybuf.len > 0 &&
-					   mybuf.data[mybuf.len - 1] == ';')
-				{
-					mybuf.data[--mybuf.len] = '\0';
-				}
-			}
-
-			/*
-			 * If SQL identifier processing was requested, then we strip out
-			 * excess double quotes and downcase unquoted letters.
-			 * Doubled double-quotes become output double-quotes, per spec.
-			 *
-			 * Note that a string like FOO"BAR"BAZ will be converted to
-			 * fooBARbaz; this is somewhat inconsistent with the SQL spec,
-			 * which would have us parse it as several identifiers.  But
-			 * for psql's purposes, we want a string like "foo"."bar" to
-			 * be treated as one option, so there's little choice.
-			 */
-			if (type == OT_SQLID || type == OT_SQLIDHACK)
-			{
-				bool		inquotes = false;
-				char	   *cp = mybuf.data;
-
-				while (*cp)
-				{
-					if (*cp == '"')
-					{
-						if (inquotes && cp[1] == '"')
-						{
-							/* Keep the first quote, remove the second */
-							cp++;
-						}
-						inquotes = !inquotes;
-						/* Collapse out quote at *cp */
-						memmove(cp, cp + 1, strlen(cp));
-						mybuf.len--;
-						/* do not advance cp */
-					}
-					else
-					{
-						if (!inquotes && type == OT_SQLID)
-							*cp = pg_tolower((unsigned char) *cp);
-						cp += PQmblen(cp, pset.encoding);
-					}
-				}
-			}
-			break;
-		case xslashquote:
-		case xslashbackquote:
-		case xslashdquote:
-			/* must have hit EOL inside quotes */
-			psql_error("unterminated quoted string\n");
-			termPQExpBuffer(&mybuf);
-			return NULL;
-		case xslashwholeline:
-			/* always okay */
-			break;
-		default:
-			/* can't get here */
-			fprintf(stderr, "invalid YY_START\n");
-			exit(1);
-	}
-
-	/*
-	 * An unquoted empty argument isn't possible unless we are at end of
-	 * command.  Return NULL instead.
-	 */
-	if (mybuf.len == 0 && *quote == 0)
-	{
-		termPQExpBuffer(&mybuf);
-		return NULL;
-	}
-
-	/* Else return the completed string. */
-	return mybuf.data;
-}
-
-/*
- * Eat up any unused \\ to complete a backslash command.
- */
-void
-psql_scan_slash_command_end(PsqlScanState state)
-{
-	/* Must be scanning already */
-	Assert(state->scanbufhandle != NULL);
-
-	/* Set up static variables that will be used by yylex */
-	cur_state = state;
-	output_buf = NULL;
-
-	if (state->buffer_stack != NULL)
-		yy_switch_to_buffer(state->buffer_stack->buf);
-	else
-		yy_switch_to_buffer(state->scanbufhandle);
-
-	BEGIN(xslashend);
-
-	/* And lex. */
-	yylex();
-
-	/* There are no possible errors in this lex state... */
-}
-
-/*
- * Evaluate a backticked substring of a slash command's argument.
- *
- * The portion of output_buf starting at backtick_start_offset is evaluated
- * as a shell command and then replaced by the command's output.
- */
-static void
-evaluate_backtick(void)
-{
-	char	   *cmd = output_buf->data + backtick_start_offset;
-	PQExpBufferData cmd_output;
-	FILE	   *fd;
-	bool		error = false;
-	char		buf[512];
-	size_t		result;
-
-	initPQExpBuffer(&cmd_output);
-
-	fd = popen(cmd, PG_BINARY_R);
-	if (!fd)
-	{
-		psql_error("%s: %s\n", cmd, strerror(errno));
-		error = true;
-	}
-
-	if (!error)
-	{
-		do
-		{
-			result = fread(buf, 1, sizeof(buf), fd);
-			if (ferror(fd))
-			{
-				psql_error("%s: %s\n", cmd, strerror(errno));
-				error = true;
-				break;
-			}
-			appendBinaryPQExpBuffer(&cmd_output, buf, result);
-		} while (!feof(fd));
-	}
-
-	if (fd && pclose(fd) == -1)
-	{
-		psql_error("%s: %s\n", cmd, strerror(errno));
-		error = true;
-	}
-
-	if (PQExpBufferDataBroken(cmd_output))
-	{
-		psql_error("%s: out of memory\n", cmd);
-		error = true;
-	}
-
-	/* Now done with cmd, delete it from output_buf */
-	output_buf->len = backtick_start_offset;
-	output_buf->data[output_buf->len] = '\0';
-
-	/* If no error, transfer result to output_buf */
-	if (!error)
-	{
-		/* strip any trailing newline */
-		if (cmd_output.len > 0 &&
-			cmd_output.data[cmd_output.len - 1] == '\n')
-			cmd_output.len--;
-		appendBinaryPQExpBuffer(output_buf, cmd_output.data, cmd_output.len);
-	}
-
-	termPQExpBuffer(&cmd_output);
-}
-
-/*
  * Push the given string onto the stack of stuff to scan.
  *
@@ -1753,5 +1206,5 @@
  * NOTE SIDE EFFECT: the new buffer is made the active flex input buffer.
  */
-static void
+void
 push_new_buffer(const char *newstr, const char *varname)
 {
@@ -1770,5 +1223,5 @@
 									&stackelem->bufstring);
 	cur_state->curline = stackelem->bufstring;
-	if (cur_state->safe_encoding)
+	if (ENC_IS_SAFE(cur_state))
 	{
 		stackelem->origstring = NULL;
@@ -1790,5 +1243,5 @@
  * switch to an appropriate buffer to continue lexing.
  */
-static void
+void
 pop_buffer_stack(PsqlScanState state)
 {
@@ -1809,5 +1262,5 @@
  * currently being scanned
  */
-static bool
+bool
 var_is_current_source(PsqlScanState state, const char *varname)
 {
@@ -1833,5 +1286,5 @@
  * NOTE SIDE EFFECT: the new buffer is made the active flex input buffer.
  */
-static YY_BUFFER_STATE
+YY_BUFFER_STATE
 prepare_buffer(const char *txt, int len, char **txtcopy)
 {
@@ -1843,5 +1296,5 @@
 	newtxt[len] = newtxt[len + 1] = YY_END_OF_BUFFER_CHAR;
 
-	if (cur_state->safe_encoding)
+	if (ENC_IS_SAFE(cur_state))
 		memcpy(newtxt, txt, len);
 	else
@@ -1852,5 +1305,5 @@
 		while (i < len)
 		{
-			int		thislen = PQmblen(txt + i, cur_state->encoding);
+			int		thislen = cur_state->cb.enc_mblen(txt + i);
 
 			/* first byte should always be okay... */
@@ -1862,9 +1315,9 @@
 	}
 
-	return yy_scan_buffer(newtxt, len + 2);
+	return cur_state->my_yy_scan_buffer(newtxt, len + 2);
 }
 
 /*
- * emit() --- body for ECHO macro
+ * scan_emit() --- body for ECHO macro
  *
  * NB: this must be used for ALL and ONLY the text copied from the flex
@@ -1873,8 +1326,8 @@
  * appended directly to output_buf.
  */
-static void
-emit(const char *txt, int len)
+void
+scan_emit(const char *txt, int len)
 {
-	if (cur_state->safe_encoding)
+	if (ENC_IS_SAFE(cur_state))
 		appendBinaryPQExpBuffer(output_buf, txt, len);
 	else
@@ -1900,13 +1353,13 @@
  * extract_substring --- fetch the true value of (part of) the current token
  *
- * This is like emit(), except that the data is returned as a malloc'd string
- * rather than being pushed directly to output_buf.
+ * This is like scan_emit(), except that the data is returned as a malloc'd
+ * string rather than being pushed directly to output_buf.
  */
-static char *
+char *
 extract_substring(const char *txt, int len)
 {
 	char	   *result = (char *) pg_malloc(len + 1);
 
-	if (cur_state->safe_encoding)
+	if (ENC_IS_SAFE(cur_state))
 		memcpy(result, txt, len);
 	else
@@ -1939,43 +1392,24 @@
  * find the variable or the escaping function fails, emit the token as-is.
  */
-static void
+void
 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);
-
-	/* Escaping. */
-	if (value)
+	/* Variable lookup if possible. */
+	if (cur_state->cb.get_variable)
 	{
-		if (!pset.db)
-			psql_error("can't escape without active connection\n");
-		else
-		{
-			char   *escaped_value;
+		char		*varname;
+		const char  *value;
+		void	   (*free_fn)(void *);
+
+		varname = extract_substring(yytext + 2, yyleng - 3);
+		value = cur_state->cb.get_variable(varname, true, as_ident, &free_fn);
+		free(varname);
 
-			if (as_ident)
-				escaped_value =
-					PQescapeIdentifier(pset.db, value, strlen(value));
-			else
-				escaped_value =
-					PQescapeLiteral(pset.db, value, strlen(value));
-
-			if (escaped_value == NULL)
-			{
-				const char *error = PQerrorMessage(pset.db);
-
-				psql_error("%s", error);
-			}
-			else
-			{
-				appendPQExpBufferStr(output_buf, escaped_value);
-				PQfreemem(escaped_value);
-				return;
-			}
+		if (value)
+		{
+			appendPQExpBufferStr(output_buf, value);
+			if (free_fn)
+				free_fn((void*)value);
+			return;
 		}
 	}
@@ -1985,4 +1419,20 @@
 	 * original text into the output buffer.
 	 */
-	emit(yytext, yyleng);
+	scan_emit(yytext, yyleng);
+}
+
+/* Default error output function */
+static void psql_error_errout(const char *fmt, ...)
+{
+	va_list	ap;
+
+	va_start(ap, fmt);
+	vfprintf(stderr, _(fmt), ap);
+	va_end(ap);
+}
+
+/* Default function to check standard_conforming_strings */
+static bool psql_standard_strings(void)
+{
+	return false;
 }
