v2-0011-plpgsql-reentrant-scanner.patch

text/plain

Filename: v2-0011-plpgsql-reentrant-scanner.patch
Type: text/plain
Part: 10
Message: Re: pure parsers and reentrant scanners

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: format-patch
Series: patch v2-0011
Subject: plpgsql: reentrant scanner
File+
src/pl/plpgsql/src/pl_comp.c 10 8
src/pl/plpgsql/src/pl_gram.y 218 202
src/pl/plpgsql/src/plpgsql.h 8 8
src/pl/plpgsql/src/pl_scanner.c 20 18
From 0728266e298c78e9943cf6f7ac81cd187c236f90 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Sat, 7 Dec 2024 00:09:08 +0100
Subject: [PATCH v2 11/11] plpgsql: reentrant scanner

---
 src/pl/plpgsql/src/pl_comp.c    |  18 +-
 src/pl/plpgsql/src/pl_gram.y    | 420 +++++++++++++++++---------------
 src/pl/plpgsql/src/pl_scanner.c |  38 +--
 src/pl/plpgsql/src/plpgsql.h    |  16 +-
 4 files changed, 256 insertions(+), 236 deletions(-)

diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 6255a86d75b..917daebef06 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -269,6 +269,7 @@ do_compile(FunctionCallInfo fcinfo,
 	Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup);
 	bool		is_dml_trigger = CALLED_AS_TRIGGER(fcinfo);
 	bool		is_event_trigger = CALLED_AS_EVENT_TRIGGER(fcinfo);
+	yyscan_t	scanner;
 	Datum		prosrcdatum;
 	char	   *proc_source;
 	HeapTuple	typeTup;
@@ -292,11 +293,11 @@ do_compile(FunctionCallInfo fcinfo,
 	/*
 	 * Setup the scanner input and error info.  We assume that this function
 	 * cannot be invoked recursively, so there's no need to save and restore
-	 * the static variables used here.
+	 * the static variables used here. XXX
 	 */
 	prosrcdatum = SysCacheGetAttrNotNull(PROCOID, procTup, Anum_pg_proc_prosrc);
 	proc_source = TextDatumGetCString(prosrcdatum);
-	plpgsql_scanner_init(proc_source);
+	scanner = plpgsql_scanner_init(proc_source);
 
 	plpgsql_error_funcname = pstrdup(NameStr(procStruct->proname));
 
@@ -779,12 +780,12 @@ do_compile(FunctionCallInfo fcinfo,
 	/*
 	 * Now parse the function's text
 	 */
-	parse_rc = plpgsql_yyparse();
+	parse_rc = plpgsql_yyparse(scanner);
 	if (parse_rc != 0)
 		elog(ERROR, "plpgsql parser returned %d", parse_rc);
 	function->action = plpgsql_parse_result;
 
-	plpgsql_scanner_finish();
+	plpgsql_scanner_finish(scanner);
 	pfree(proc_source);
 
 	/*
@@ -841,6 +842,7 @@ do_compile(FunctionCallInfo fcinfo,
 PLpgSQL_function *
 plpgsql_compile_inline(char *proc_source)
 {
+	yyscan_t	scanner;
 	char	   *func_name = "inline_code_block";
 	PLpgSQL_function *function;
 	ErrorContextCallback plerrcontext;
@@ -851,9 +853,9 @@ plpgsql_compile_inline(char *proc_source)
 	/*
 	 * Setup the scanner input and error info.  We assume that this function
 	 * cannot be invoked recursively, so there's no need to save and restore
-	 * the static variables used here.
+	 * the static variables used here. XXX
 	 */
-	plpgsql_scanner_init(proc_source);
+	scanner = plpgsql_scanner_init(proc_source);
 
 	plpgsql_error_funcname = func_name;
 
@@ -935,12 +937,12 @@ plpgsql_compile_inline(char *proc_source)
 	/*
 	 * Now parse the function's text
 	 */
-	parse_rc = plpgsql_yyparse();
+	parse_rc = plpgsql_yyparse(scanner);
 	if (parse_rc != 0)
 		elog(ERROR, "plpgsql parser returned %d", parse_rc);
 	function->action = plpgsql_parse_result;
 
-	plpgsql_scanner_finish();
+	plpgsql_scanner_finish(scanner);
 
 	/*
 	 * If it returns VOID (always true at the moment), we allow control to
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index 8182ce28aa1..4d09fa89d99 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -63,7 +63,7 @@ static	bool			tok_is_keyword(int token, union YYSTYPE *lval,
 									   int kw_token, const char *kw_str);
 static	void			word_is_not_variable(PLword *word, int location);
 static	void			cword_is_not_variable(PLcword *cword, int location);
-static	void			current_token_is_not_variable(int tok);
+static	void			current_token_is_not_variable(int tok, yyscan_t yyscanner);
 static	PLpgSQL_expr	*read_sql_construct(int until,
 											int until2,
 											int until3,
@@ -72,31 +72,33 @@ static	PLpgSQL_expr	*read_sql_construct(int until,
 											bool isexpression,
 											bool valid_sql,
 											int *startloc,
-											int *endtoken);
+											int *endtoken,
+											yyscan_t yyscanner);
 static	PLpgSQL_expr	*read_sql_expression(int until,
-											 const char *expected);
+											 const char *expected, yyscan_t yyscanner);
 static	PLpgSQL_expr	*read_sql_expression2(int until, int until2,
 											  const char *expected,
-											  int *endtoken);
-static	PLpgSQL_expr	*read_sql_stmt(void);
-static	PLpgSQL_type	*read_datatype(int tok);
+											  int *endtoken, yyscan_t yyscanner);
+static	PLpgSQL_expr	*read_sql_stmt(yyscan_t yyscanner);
+static	PLpgSQL_type	*read_datatype(int tok, yyscan_t yyscanner);
 static	PLpgSQL_stmt	*make_execsql_stmt(int firsttoken, int location,
-										   PLword *word);
-static	PLpgSQL_stmt_fetch *read_fetch_direction(void);
+										   PLword *word, yyscan_t yyscanner);
+static	PLpgSQL_stmt_fetch *read_fetch_direction(yyscan_t yyscanner);
 static	void			 complete_direction(PLpgSQL_stmt_fetch *fetch,
-											bool *check_FROM);
-static	PLpgSQL_stmt	*make_return_stmt(int location);
-static	PLpgSQL_stmt	*make_return_next_stmt(int location);
-static	PLpgSQL_stmt	*make_return_query_stmt(int location);
+											bool *check_FROM, yyscan_t yyscanner);
+static	PLpgSQL_stmt	*make_return_stmt(int location, yyscan_t yyscanner);
+static	PLpgSQL_stmt	*make_return_next_stmt(int location, yyscan_t yyscanner);
+static	PLpgSQL_stmt	*make_return_query_stmt(int location, yyscan_t yyscanner);
 static  PLpgSQL_stmt	*make_case(int location, PLpgSQL_expr *t_expr,
 								   List *case_when_list, List *else_stmts);
 static	char			*NameOfDatum(PLwdatum *wdatum);
 static	void			 check_assignable(PLpgSQL_datum *datum, int location);
 static	void			 read_into_target(PLpgSQL_variable **target,
-										  bool *strict);
+										  bool *strict, yyscan_t yyscanner);
 static	PLpgSQL_row		*read_into_scalar_list(char *initial_name,
 											   PLpgSQL_datum *initial_datum,
-											   int initial_location);
+											   int initial_location,
+											   yyscan_t yyscanner);
 static	PLpgSQL_row		*make_scalar_list1(char *initial_name,
 										   PLpgSQL_datum *initial_datum,
 										   int lineno, int location);
@@ -108,12 +110,14 @@ static	void			 check_labels(const char *start_label,
 									  const char *end_label,
 									  int end_location);
 static	PLpgSQL_expr	*read_cursor_args(PLpgSQL_var *cursor,
-										  int until);
-static	List			*read_raise_options(void);
+										  int until, yyscan_t yyscanner);
+static	List			*read_raise_options(yyscan_t yyscanner);
 static	void			check_raise_parameters(PLpgSQL_stmt_raise *stmt);
 
 %}
 
+%parse-param {yyscan_t yyscanner}
+%lex-param   {yyscan_t yyscanner}
 %expect 0
 %name-prefix="plpgsql_yy"
 %locations
@@ -577,7 +581,7 @@ opt_scrollable :
 
 decl_cursor_query :
 					{
-						$$ = read_sql_stmt();
+						$$ = read_sql_stmt(yyscanner);
 					}
 				;
 
@@ -706,7 +710,7 @@ decl_varname	: T_WORD
 						if (plpgsql_ns_lookup(plpgsql_ns_top(), true,
 											  $1.ident, NULL, NULL,
 											  NULL) != NULL)
-							yyerror("duplicate declaration");
+							yyerror(yyscanner, "duplicate declaration");
 
 						if (plpgsql_curr_compile->extra_warnings & PLPGSQL_XCHECK_SHADOWVAR ||
 							plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_SHADOWVAR)
@@ -734,7 +738,7 @@ decl_varname	: T_WORD
 						if (plpgsql_ns_lookup(plpgsql_ns_top(), true,
 											  $1, NULL, NULL,
 											  NULL) != NULL)
-							yyerror("duplicate declaration");
+							yyerror(yyscanner, "duplicate declaration");
 
 						if (plpgsql_curr_compile->extra_warnings & PLPGSQL_XCHECK_SHADOWVAR ||
 							plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_SHADOWVAR)
@@ -766,7 +770,7 @@ decl_datatype	:
 						 * consume it, and then we must tell bison to forget
 						 * it.
 						 */
-						$$ = read_datatype(yychar);
+						$$ = read_datatype(yychar, yyscanner);
 						yyclearin;
 					}
 				;
@@ -799,7 +803,7 @@ decl_defval		: ';'
 					{ $$ = NULL; }
 				| decl_defkey
 					{
-						$$ = read_sql_expression(';', ";");
+						$$ = read_sql_expression(';', ";", yyscanner);
 					}
 				;
 
@@ -900,7 +904,8 @@ stmt_perform	: K_PERFORM
 						new->expr = read_sql_construct(';', 0, 0, ";",
 													   RAW_PARSE_DEFAULT,
 													   false, false,
-													   &startloc, NULL);
+													   &startloc, NULL,
+													   yyscanner);
 						/* overwrite "perform" ... */
 						memcpy(new->expr->query, " SELECT", 7);
 						/* left-justify to get rid of the leading space */
@@ -923,7 +928,7 @@ stmt_call		: K_CALL
 						new->lineno = plpgsql_location_to_lineno(@1);
 						new->stmtid = ++plpgsql_curr_compile->nstatements;
 						plpgsql_push_back_token(K_CALL);
-						new->expr = read_sql_stmt();
+						new->expr = read_sql_stmt(yyscanner);
 						new->is_call = true;
 
 						/* Remember we may need a procedure resource owner */
@@ -942,7 +947,7 @@ stmt_call		: K_CALL
 						new->lineno = plpgsql_location_to_lineno(@1);
 						new->stmtid = ++plpgsql_curr_compile->nstatements;
 						plpgsql_push_back_token(K_DO);
-						new->expr = read_sql_stmt();
+						new->expr = read_sql_stmt(yyscanner);
 						new->is_call = false;
 
 						/* Remember we may need a procedure resource owner */
@@ -986,7 +991,8 @@ stmt_assign		: T_DATUM
 						new->expr = read_sql_construct(';', 0, 0, ";",
 													   pmode,
 													   false, true,
-													   NULL, NULL);
+													   NULL, NULL,
+													   yyscanner);
 
 						$$ = (PLpgSQL_stmt *) new;
 					}
@@ -1093,7 +1099,7 @@ getdiag_list_item : getdiag_target assign_operator getdiag_item
 
 getdiag_item :
 					{
-						int			tok = yylex();
+						int			tok = yylex(yyscanner);
 
 						if (tok_is_keyword(tok, &yylval,
 										   K_ROW_COUNT, "row_count"))
@@ -1135,7 +1141,7 @@ getdiag_item :
 												K_RETURNED_SQLSTATE, "returned_sqlstate"))
 							$$ = PLPGSQL_GETDIAG_RETURNED_SQLSTATE;
 						else
-							yyerror("unrecognized GET DIAGNOSTICS item");
+							yyerror(yyscanner, "unrecognized GET DIAGNOSTICS item");
 					}
 				;
 
@@ -1148,7 +1154,7 @@ getdiag_target	: T_DATUM
 						 */
 						if ($1.datum->dtype == PLPGSQL_DTYPE_ROW ||
 							$1.datum->dtype == PLPGSQL_DTYPE_REC ||
-							plpgsql_peek() == '[')
+							plpgsql_peek(yyscanner) == '[')
 							ereport(ERROR,
 									(errcode(ERRCODE_SYNTAX_ERROR),
 									 errmsg("\"%s\" is not a scalar variable",
@@ -1222,12 +1228,12 @@ stmt_case		: K_CASE opt_expr_until_when case_when_list opt_case_else K_END K_CAS
 opt_expr_until_when	:
 					{
 						PLpgSQL_expr *expr = NULL;
-						int			tok = yylex();
+						int			tok = yylex(yyscanner);
 
 						if (tok != K_WHEN)
 						{
 							plpgsql_push_back_token(tok);
-							expr = read_sql_expression(K_WHEN, "WHEN");
+							expr = read_sql_expression(K_WHEN, "WHEN", yyscanner);
 						}
 						plpgsql_push_back_token(K_WHEN);
 						$$ = expr;
@@ -1347,7 +1353,7 @@ stmt_for		: opt_loop_label K_FOR for_control loop_body
 
 for_control		: for_variable K_IN
 					{
-						int			tok = yylex();
+						int			tok = yylex(yyscanner);
 						int			tokloc = yylloc;
 
 						if (tok == K_EXECUTE)
@@ -1359,7 +1365,7 @@ for_control		: for_variable K_IN
 
 							expr = read_sql_expression2(K_LOOP, K_USING,
 														"LOOP or USING",
-														&term);
+														&term, yyscanner);
 
 							new = palloc0(sizeof(PLpgSQL_stmt_dynfors));
 							new->cmd_type = PLPGSQL_STMT_DYNFORS;
@@ -1392,7 +1398,7 @@ for_control		: for_variable K_IN
 								{
 									expr = read_sql_expression2(',', K_LOOP,
 																", or LOOP",
-																&term);
+																&term, yyscanner);
 									new->params = lappend(new->params, expr);
 								} while (term == ',');
 							}
@@ -1427,8 +1433,7 @@ for_control		: for_variable K_IN
 										 parser_errposition(tokloc)));
 
 							/* collect cursor's parameters if any */
-							new->argquery = read_cursor_args(cursor,
-															 K_LOOP);
+							new->argquery = read_cursor_args(cursor, K_LOOP, yyscanner);
 
 							/* create loop's private RECORD variable */
 							new->var = (PLpgSQL_variable *)
@@ -1479,7 +1484,8 @@ for_control		: for_variable K_IN
 													   true,
 													   false,
 													   &expr1loc,
-													   &tok);
+													   &tok,
+													   yyscanner);
 
 							if (tok == DOT_DOT)
 							{
@@ -1500,12 +1506,12 @@ for_control		: for_variable K_IN
 								/* Read and check the second one */
 								expr2 = read_sql_expression2(K_LOOP, K_BY,
 															 "LOOP",
-															 &tok);
+															 &tok, yyscanner);
 
 								/* Get the BY clause if any */
 								if (tok == K_BY)
 									expr_by = read_sql_expression(K_LOOP,
-																  "LOOP");
+																  "LOOP", yyscanner);
 								else
 									expr_by = NULL;
 
@@ -1620,13 +1626,14 @@ for_variable	: T_DATUM
 							$$.scalar = $1.datum;
 							$$.row = NULL;
 							/* check for comma-separated list */
-							tok = yylex();
+							tok = yylex(yyscanner);
 							plpgsql_push_back_token(tok);
 							if (tok == ',')
 								$$.row = (PLpgSQL_datum *)
 									read_into_scalar_list($$.name,
 														  $$.scalar,
-														  @1);
+														  @1,
+														  yyscanner);
 						}
 					}
 				| T_WORD
@@ -1638,7 +1645,7 @@ for_variable	: T_DATUM
 						$$.scalar = NULL;
 						$$.row = NULL;
 						/* check for comma-separated list */
-						tok = yylex();
+						tok = yylex(yyscanner);
 						plpgsql_push_back_token(tok);
 						if (tok == ',')
 							word_is_not_variable(&($1), @1);
@@ -1765,24 +1772,24 @@ stmt_return		: K_RETURN
 					{
 						int			tok;
 
-						tok = yylex();
+						tok = yylex(yyscanner);
 						if (tok == 0)
-							yyerror("unexpected end of function definition");
+							yyerror(yyscanner, "unexpected end of function definition");
 
 						if (tok_is_keyword(tok, &yylval,
 										   K_NEXT, "next"))
 						{
-							$$ = make_return_next_stmt(@1);
+							$$ = make_return_next_stmt(@1, yyscanner);
 						}
 						else if (tok_is_keyword(tok, &yylval,
 												K_QUERY, "query"))
 						{
-							$$ = make_return_query_stmt(@1);
+							$$ = make_return_query_stmt(@1, yyscanner);
 						}
 						else
 						{
 							plpgsql_push_back_token(tok);
-							$$ = make_return_stmt(@1);
+							$$ = make_return_stmt(@1, yyscanner);
 						}
 					}
 				;
@@ -1803,9 +1810,9 @@ stmt_raise		: K_RAISE
 						new->params = NIL;
 						new->options = NIL;
 
-						tok = yylex();
+						tok = yylex(yyscanner);
 						if (tok == 0)
-							yyerror("unexpected end of function definition");
+							yyerror(yyscanner, "unexpected end of function definition");
 
 						/*
 						 * We could have just RAISE, meaning to re-throw
@@ -1820,40 +1827,40 @@ stmt_raise		: K_RAISE
 											   K_EXCEPTION, "exception"))
 							{
 								new->elog_level = ERROR;
-								tok = yylex();
+								tok = yylex(yyscanner);
 							}
 							else if (tok_is_keyword(tok, &yylval,
 													K_WARNING, "warning"))
 							{
 								new->elog_level = WARNING;
-								tok = yylex();
+								tok = yylex(yyscanner);
 							}
 							else if (tok_is_keyword(tok, &yylval,
 													K_NOTICE, "notice"))
 							{
 								new->elog_level = NOTICE;
-								tok = yylex();
+								tok = yylex(yyscanner);
 							}
 							else if (tok_is_keyword(tok, &yylval,
 													K_INFO, "info"))
 							{
 								new->elog_level = INFO;
-								tok = yylex();
+								tok = yylex(yyscanner);
 							}
 							else if (tok_is_keyword(tok, &yylval,
 													K_LOG, "log"))
 							{
 								new->elog_level = LOG;
-								tok = yylex();
+								tok = yylex(yyscanner);
 							}
 							else if (tok_is_keyword(tok, &yylval,
 													K_DEBUG, "debug"))
 							{
 								new->elog_level = DEBUG1;
-								tok = yylex();
+								tok = yylex(yyscanner);
 							}
 							if (tok == 0)
-								yyerror("unexpected end of function definition");
+								yyerror(yyscanner, "unexpected end of function definition");
 
 							/*
 							 * Next we can have a condition name, or
@@ -1871,9 +1878,9 @@ stmt_raise		: K_RAISE
 								 * begins the list of parameter expressions,
 								 * or USING to begin the options list.
 								 */
-								tok = yylex();
+								tok = yylex(yyscanner);
 								if (tok != ',' && tok != ';' && tok != K_USING)
-									yyerror("syntax error");
+									yyerror(yyscanner, "syntax error");
 
 								while (tok == ',')
 								{
@@ -1883,7 +1890,8 @@ stmt_raise		: K_RAISE
 															  ", or ; or USING",
 															  RAW_PARSE_PLPGSQL_EXPR,
 															  true, true,
-															  NULL, &tok);
+															  NULL, &tok,
+															  yyscanner);
 									new->params = lappend(new->params, expr);
 								}
 							}
@@ -1896,14 +1904,14 @@ stmt_raise		: K_RAISE
 									/* next token should be a string literal */
 									char	   *sqlstatestr;
 
-									if (yylex() != SCONST)
-										yyerror("syntax error");
+									if (yylex(yyscanner) != SCONST)
+										yyerror(yyscanner, "syntax error");
 									sqlstatestr = yylval.str;
 
 									if (strlen(sqlstatestr) != 5)
-										yyerror("invalid SQLSTATE code");
+										yyerror(yyscanner, "invalid SQLSTATE code");
 									if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5)
-										yyerror("invalid SQLSTATE code");
+										yyerror(yyscanner, "invalid SQLSTATE code");
 									new->condname = sqlstatestr;
 								}
 								else
@@ -1913,17 +1921,17 @@ stmt_raise		: K_RAISE
 									else if (plpgsql_token_is_unreserved_keyword(tok))
 										new->condname = pstrdup(yylval.keyword);
 									else
-										yyerror("syntax error");
+										yyerror(yyscanner, "syntax error");
 									plpgsql_recognize_err_condition(new->condname,
 																	false);
 								}
-								tok = yylex();
+								tok = yylex(yyscanner);
 								if (tok != ';' && tok != K_USING)
-									yyerror("syntax error");
+									yyerror(yyscanner, "syntax error");
 							}
 
 							if (tok == K_USING)
-								new->options = read_raise_options();
+								new->options = read_raise_options(yyscanner);
 						}
 
 						check_raise_parameters(new);
@@ -1945,10 +1953,10 @@ stmt_assert		: K_ASSERT
 
 						new->cond = read_sql_expression2(',', ';',
 														 ", or ;",
-														 &tok);
+														 &tok, yyscanner);
 
 						if (tok == ',')
-							new->message = read_sql_expression(';', ";");
+							new->message = read_sql_expression(';', ";", yyscanner);
 						else
 							new->message = NULL;
 
@@ -1976,37 +1984,37 @@ loop_body		: proc_sect K_END K_LOOP opt_label ';'
  */
 stmt_execsql	: K_IMPORT
 					{
-						$$ = make_execsql_stmt(K_IMPORT, @1, NULL);
+						$$ = make_execsql_stmt(K_IMPORT, @1, NULL, yyscanner);
 					}
 				| K_INSERT
 					{
-						$$ = make_execsql_stmt(K_INSERT, @1, NULL);
+						$$ = make_execsql_stmt(K_INSERT, @1, NULL, yyscanner);
 					}
 				| K_MERGE
 					{
-						$$ = make_execsql_stmt(K_MERGE, @1, NULL);
+						$$ = make_execsql_stmt(K_MERGE, @1, NULL, yyscanner);
 					}
 				| T_WORD
 					{
 						int			tok;
 
-						tok = yylex();
+						tok = yylex(yyscanner);
 						plpgsql_push_back_token(tok);
 						if (tok == '=' || tok == COLON_EQUALS ||
 							tok == '[' || tok == '.')
 							word_is_not_variable(&($1), @1);
-						$$ = make_execsql_stmt(T_WORD, @1, &($1));
+						$$ = make_execsql_stmt(T_WORD, @1, &($1), yyscanner);
 					}
 				| T_CWORD
 					{
 						int			tok;
 
-						tok = yylex();
+						tok = yylex(yyscanner);
 						plpgsql_push_back_token(tok);
 						if (tok == '=' || tok == COLON_EQUALS ||
 							tok == '[' || tok == '.')
 							cword_is_not_variable(&($1), @1);
-						$$ = make_execsql_stmt(T_CWORD, @1, NULL);
+						$$ = make_execsql_stmt(T_CWORD, @1, NULL, yyscanner);
 					}
 				;
 
@@ -2020,7 +2028,8 @@ stmt_dynexecute : K_EXECUTE
 												  "INTO or USING or ;",
 												  RAW_PARSE_PLPGSQL_EXPR,
 												  true, true,
-												  NULL, &endtoken);
+												  NULL, &endtoken,
+												  yyscanner);
 
 						new = palloc(sizeof(PLpgSQL_stmt_dynexecute));
 						new->cmd_type = PLPGSQL_STMT_DYNEXECUTE;
@@ -2044,29 +2053,30 @@ stmt_dynexecute : K_EXECUTE
 							if (endtoken == K_INTO)
 							{
 								if (new->into)			/* multiple INTO */
-									yyerror("syntax error");
+									yyerror(yyscanner, "syntax error");
 								new->into = true;
-								read_into_target(&new->target, &new->strict);
-								endtoken = yylex();
+								read_into_target(&new->target, &new->strict, yyscanner);
+								endtoken = yylex(yyscanner);
 							}
 							else if (endtoken == K_USING)
 							{
 								if (new->params)		/* multiple USING */
-									yyerror("syntax error");
+									yyerror(yyscanner, "syntax error");
 								do
 								{
 									expr = read_sql_construct(',', ';', K_INTO,
 															  ", or ; or INTO",
 															  RAW_PARSE_PLPGSQL_EXPR,
 															  true, true,
-															  NULL, &endtoken);
+															  NULL, &endtoken,
+															  yyscanner);
 									new->params = lappend(new->params, expr);
 								} while (endtoken == ',');
 							}
 							else if (endtoken == ';')
 								break;
 							else
-								yyerror("syntax error");
+								yyerror(yyscanner, "syntax error");
 						}
 
 						$$ = (PLpgSQL_stmt *) new;
@@ -2089,29 +2099,29 @@ stmt_open		: K_OPEN cursor_variable
 						if ($2->cursor_explicit_expr == NULL)
 						{
 							/* be nice if we could use opt_scrollable here */
-							tok = yylex();
+							tok = yylex(yyscanner);
 							if (tok_is_keyword(tok, &yylval,
 											   K_NO, "no"))
 							{
-								tok = yylex();
+								tok = yylex(yyscanner);
 								if (tok_is_keyword(tok, &yylval,
 												   K_SCROLL, "scroll"))
 								{
 									new->cursor_options |= CURSOR_OPT_NO_SCROLL;
-									tok = yylex();
+									tok = yylex(yyscanner);
 								}
 							}
 							else if (tok_is_keyword(tok, &yylval,
 													K_SCROLL, "scroll"))
 							{
 								new->cursor_options |= CURSOR_OPT_SCROLL;
-								tok = yylex();
+								tok = yylex(yyscanner);
 							}
 
 							if (tok != K_FOR)
-								yyerror("syntax error, expected \"FOR\"");
+								yyerror(yyscanner, "syntax error, expected \"FOR\"");
 
-							tok = yylex();
+							tok = yylex(yyscanner);
 							if (tok == K_EXECUTE)
 							{
 								int			endtoken;
@@ -2119,7 +2129,7 @@ stmt_open		: K_OPEN cursor_variable
 								new->dynquery =
 									read_sql_expression2(K_USING, ';',
 														 "USING or ;",
-														 &endtoken);
+														 &endtoken, yyscanner);
 
 								/* If we found "USING", collect argument(s) */
 								if (endtoken == K_USING)
@@ -2130,7 +2140,7 @@ stmt_open		: K_OPEN cursor_variable
 									{
 										expr = read_sql_expression2(',', ';',
 																	", or ;",
-																	&endtoken);
+																	&endtoken, yyscanner);
 										new->params = lappend(new->params,
 															  expr);
 									} while (endtoken == ',');
@@ -2139,13 +2149,13 @@ stmt_open		: K_OPEN cursor_variable
 							else
 							{
 								plpgsql_push_back_token(tok);
-								new->query = read_sql_stmt();
+								new->query = read_sql_stmt(yyscanner);
 							}
 						}
 						else
 						{
 							/* predefined cursor query, so read args */
-							new->argquery = read_cursor_args($2, ';');
+							new->argquery = read_cursor_args($2, ';', yyscanner);
 						}
 
 						$$ = (PLpgSQL_stmt *) new;
@@ -2158,10 +2168,10 @@ stmt_fetch		: K_FETCH opt_fetch_direction cursor_variable K_INTO
 						PLpgSQL_variable *target;
 
 						/* We have already parsed everything through the INTO keyword */
-						read_into_target(&target, NULL);
+						read_into_target(&target, NULL, yyscanner);
 
-						if (yylex() != ';')
-							yyerror("syntax error");
+						if (yylex(yyscanner) != ';')
+							yyerror(yyscanner, "syntax error");
 
 						/*
 						 * We don't allow multiple rows in PL/pgSQL's FETCH
@@ -2196,7 +2206,7 @@ stmt_move		: K_MOVE opt_fetch_direction cursor_variable ';'
 
 opt_fetch_direction	:
 					{
-						$$ = read_fetch_direction();
+						$$ = read_fetch_direction(yyscanner);
 					}
 				;
 
@@ -2264,7 +2274,7 @@ cursor_variable	: T_DATUM
 						 * just throw an error if next token is '['.
 						 */
 						if ($1.datum->dtype != PLPGSQL_DTYPE_VAR ||
-							plpgsql_peek() == '[')
+							plpgsql_peek(yyscanner) == '[')
 							ereport(ERROR,
 									(errcode(ERRCODE_DATATYPE_MISMATCH),
 									 errmsg("cursor variable must be a simple variable"),
@@ -2384,14 +2394,14 @@ proc_condition	: any_identifier
 								char   *sqlstatestr;
 
 								/* next token should be a string literal */
-								if (yylex() != SCONST)
-									yyerror("syntax error");
+								if (yylex(yyscanner) != SCONST)
+									yyerror(yyscanner, "syntax error");
 								sqlstatestr = yylval.str;
 
 								if (strlen(sqlstatestr) != 5)
-									yyerror("invalid SQLSTATE code");
+									yyerror(yyscanner, "invalid SQLSTATE code");
 								if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5)
-									yyerror("invalid SQLSTATE code");
+									yyerror(yyscanner, "invalid SQLSTATE code");
 
 								new = palloc(sizeof(PLpgSQL_condition));
 								new->sqlerrstate =
@@ -2409,15 +2419,15 @@ proc_condition	: any_identifier
 				;
 
 expr_until_semi :
-					{ $$ = read_sql_expression(';', ";"); }
+					{ $$ = read_sql_expression(';', ";", yyscanner); }
 				;
 
 expr_until_then :
-					{ $$ = read_sql_expression(K_THEN, "THEN"); }
+					{ $$ = read_sql_expression(K_THEN, "THEN", yyscanner); }
 				;
 
 expr_until_loop :
-					{ $$ = read_sql_expression(K_LOOP, "LOOP"); }
+					{ $$ = read_sql_expression(K_LOOP, "LOOP", yyscanner); }
 				;
 
 opt_block_label	:
@@ -2475,7 +2485,7 @@ any_identifier	: T_WORD
 				| T_DATUM
 					{
 						if ($1.ident == NULL) /* composite name not OK */
-							yyerror("syntax error");
+							yyerror(yyscanner, "syntax error");
 						$$ = $1.ident;
 					}
 				;
@@ -2627,42 +2637,45 @@ cword_is_not_variable(PLcword *cword, int location)
  * look at yylval and yylloc.
  */
 static void
-current_token_is_not_variable(int tok)
+current_token_is_not_variable(int tok, yyscan_t yyscanner)
 {
 	if (tok == T_WORD)
 		word_is_not_variable(&(yylval.word), yylloc);
 	else if (tok == T_CWORD)
 		cword_is_not_variable(&(yylval.cword), yylloc);
 	else
-		yyerror("syntax error");
+		yyerror(yyscanner, "syntax error");
 }
 
 /* Convenience routine to read an expression with one possible terminator */
 static PLpgSQL_expr *
-read_sql_expression(int until, const char *expected)
+read_sql_expression(int until, const char *expected, yyscan_t yyscanner)
 {
 	return read_sql_construct(until, 0, 0, expected,
 							  RAW_PARSE_PLPGSQL_EXPR,
-							  true, true, NULL, NULL);
+							  true, true, NULL, NULL,
+							  yyscanner);
 }
 
 /* Convenience routine to read an expression with two possible terminators */
 static PLpgSQL_expr *
 read_sql_expression2(int until, int until2, const char *expected,
-					 int *endtoken)
+					 int *endtoken, yyscan_t yyscanner)
 {
 	return read_sql_construct(until, until2, 0, expected,
 							  RAW_PARSE_PLPGSQL_EXPR,
-							  true, true, NULL, endtoken);
+							  true, true, NULL, endtoken,
+							  yyscanner);
 }
 
 /* Convenience routine to read a SQL statement that must end with ';' */
 static PLpgSQL_expr *
-read_sql_stmt(void)
+read_sql_stmt(yyscan_t yyscanner)
 {
 	return read_sql_construct(';', 0, 0, ";",
 							  RAW_PARSE_DEFAULT,
-							  false, true, NULL, NULL);
+							  false, true, NULL, NULL,
+							  yyscanner);
 }
 
 /*
@@ -2688,7 +2701,8 @@ read_sql_construct(int until,
 				   bool isexpression,
 				   bool valid_sql,
 				   int *startloc,
-				   int *endtoken)
+				   int *endtoken,
+				   yyscan_t yyscanner)
 {
 	int			tok;
 	StringInfoData ds;
@@ -2706,7 +2720,7 @@ read_sql_construct(int until,
 
 	for (;;)
 	{
-		tok = yylex();
+		tok = yylex(yyscanner);
 		if (startlocation < 0)			/* remember loc of first token */
 			startlocation = yylloc;
 		if (tok == until && parenlevel == 0)
@@ -2721,7 +2735,7 @@ read_sql_construct(int until,
 		{
 			parenlevel--;
 			if (parenlevel < 0)
-				yyerror("mismatched parentheses");
+				yyerror(yyscanner, "mismatched parentheses");
 		}
 		/*
 		 * End of function definition is an error, and we don't expect to
@@ -2731,7 +2745,7 @@ read_sql_construct(int until,
 		if (tok == 0 || tok == ';')
 		{
 			if (parenlevel != 0)
-				yyerror("mismatched parentheses");
+				yyerror(yyscanner, "mismatched parentheses");
 			if (isexpression)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
@@ -2760,9 +2774,9 @@ read_sql_construct(int until,
 	if (startlocation >= endlocation)
 	{
 		if (isexpression)
-			yyerror("missing expression");
+			yyerror(yyscanner, "missing expression");
 		else
-			yyerror("missing SQL statement");
+			yyerror(yyscanner, "missing SQL statement");
 	}
 
 	/*
@@ -2794,7 +2808,7 @@ read_sql_construct(int until,
  * Returns a PLpgSQL_type struct.
  */
 static PLpgSQL_type *
-read_datatype(int tok)
+read_datatype(int tok, yyscan_t yyscanner)
 {
 	StringInfoData ds;
 	char	   *type_name;
@@ -2807,7 +2821,7 @@ read_datatype(int tok)
 
 	/* Often there will be a lookahead token, but if not, get one */
 	if (tok == YYEMPTY)
-		tok = yylex();
+		tok = yylex(yyscanner);
 
 	/* The current token is the start of what we'll pass to parse_datatype */
 	startlocation = yylloc;
@@ -2820,10 +2834,10 @@ read_datatype(int tok)
 	{
 		char	   *dtname = yylval.word.ident;
 
-		tok = yylex();
+		tok = yylex(yyscanner);
 		if (tok == '%')
 		{
-			tok = yylex();
+			tok = yylex(yyscanner);
 			if (tok_is_keyword(tok, &yylval,
 							   K_TYPE, "type"))
 				result = plpgsql_parse_wordtype(dtname);
@@ -2836,10 +2850,10 @@ read_datatype(int tok)
 	{
 		char	   *dtname = pstrdup(yylval.keyword);
 
-		tok = yylex();
+		tok = yylex(yyscanner);
 		if (tok == '%')
 		{
-			tok = yylex();
+			tok = yylex(yyscanner);
 			if (tok_is_keyword(tok, &yylval,
 							   K_TYPE, "type"))
 				result = plpgsql_parse_wordtype(dtname);
@@ -2852,10 +2866,10 @@ read_datatype(int tok)
 	{
 		List	   *dtnames = yylval.cword.idents;
 
-		tok = yylex();
+		tok = yylex(yyscanner);
 		if (tok == '%')
 		{
-			tok = yylex();
+			tok = yylex(yyscanner);
 			if (tok_is_keyword(tok, &yylval,
 							   K_TYPE, "type"))
 				result = plpgsql_parse_cwordtype(dtnames);
@@ -2877,22 +2891,22 @@ read_datatype(int tok)
 	{
 		bool		is_array = false;
 
-		tok = yylex();
+		tok = yylex(yyscanner);
 		if (tok_is_keyword(tok, &yylval,
 						   K_ARRAY, "array"))
 		{
 			is_array = true;
-			tok = yylex();
+			tok = yylex(yyscanner);
 		}
 		while (tok == '[')
 		{
 			is_array = true;
-			tok = yylex();
+			tok = yylex(yyscanner);
 			if (tok == ICONST)
-				tok = yylex();
+				tok = yylex(yyscanner);
 			if (tok != ']')
-				yyerror("syntax error, expected \"]\"");
-			tok = yylex();
+				yyerror(yyscanner, "syntax error, expected \"]\"");
+			tok = yylex(yyscanner);
 		}
 		plpgsql_push_back_token(tok);
 
@@ -2913,9 +2927,9 @@ read_datatype(int tok)
 		if (tok == 0)
 		{
 			if (parenlevel != 0)
-				yyerror("mismatched parentheses");
+				yyerror(yyscanner, "mismatched parentheses");
 			else
-				yyerror("incomplete data type declaration");
+				yyerror(yyscanner, "incomplete data type declaration");
 		}
 		/* Possible followers for datatype in a declaration */
 		if (tok == K_COLLATE || tok == K_NOT ||
@@ -2929,7 +2943,7 @@ read_datatype(int tok)
 		else if (tok == ')')
 			parenlevel--;
 
-		tok = yylex();
+		tok = yylex(yyscanner);
 	}
 
 	/* set up ds to contain complete typename text */
@@ -2938,7 +2952,7 @@ read_datatype(int tok)
 	type_name = ds.data;
 
 	if (type_name[0] == '\0')
-		yyerror("missing data type declaration");
+		yyerror(yyscanner, "missing data type declaration");
 
 	result = parse_datatype(type_name, startlocation);
 
@@ -2955,7 +2969,7 @@ read_datatype(int tok)
  * If firsttoken == T_WORD, pass its yylval value as "word", else pass NULL.
  */
 static PLpgSQL_stmt *
-make_execsql_stmt(int firsttoken, int location, PLword *word)
+make_execsql_stmt(int firsttoken, int location, PLword *word, yyscan_t yyscanner)
 {
 	StringInfoData ds;
 	IdentifierLookup save_IdentifierLookup;
@@ -3024,7 +3038,7 @@ make_execsql_stmt(int firsttoken, int location, PLword *word)
 	for (;;)
 	{
 		prev_tok = tok;
-		tok = yylex();
+		tok = yylex(yyscanner);
 		if (have_into && into_end_loc < 0)
 			into_end_loc = yylloc;		/* token after the INTO part */
 		/* Detect CREATE [OR REPLACE] {FUNCTION|PROCEDURE} */
@@ -3063,7 +3077,7 @@ make_execsql_stmt(int firsttoken, int location, PLword *word)
 		if (tok == ';' && paren_depth == 0 && begin_depth == 0)
 			break;
 		if (tok == 0)
-			yyerror("unexpected end of function definition");
+			yyerror(yyscanner, "unexpected end of function definition");
 		if (tok == K_INTO)
 		{
 			if (prev_tok == K_INSERT)
@@ -3073,11 +3087,11 @@ make_execsql_stmt(int firsttoken, int location, PLword *word)
 			if (firsttoken == K_IMPORT)
 				continue;		/* IMPORT ... INTO is not an INTO-target */
 			if (have_into)
-				yyerror("INTO specified more than once");
+				yyerror(yyscanner, "INTO specified more than once");
 			have_into = true;
 			into_start_loc = yylloc;
 			plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL;
-			read_into_target(&target, &have_strict);
+			read_into_target(&target, &have_strict, yyscanner);
 			plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_EXPR;
 		}
 	}
@@ -3130,7 +3144,7 @@ make_execsql_stmt(int firsttoken, int location, PLword *word)
  * Read FETCH or MOVE direction clause (everything through FROM/IN).
  */
 static PLpgSQL_stmt_fetch *
-read_fetch_direction(void)
+read_fetch_direction(yyscan_t yyscanner)
 {
 	PLpgSQL_stmt_fetch *fetch;
 	int			tok;
@@ -3149,9 +3163,9 @@ read_fetch_direction(void)
 	fetch->expr = NULL;
 	fetch->returns_multiple_rows = false;
 
-	tok = yylex();
+	tok = yylex(yyscanner);
 	if (tok == 0)
-		yyerror("unexpected end of function definition");
+		yyerror(yyscanner, "unexpected end of function definition");
 
 	if (tok_is_keyword(tok, &yylval,
 					   K_NEXT, "next"))
@@ -3180,7 +3194,7 @@ read_fetch_direction(void)
 		fetch->direction = FETCH_ABSOLUTE;
 		fetch->expr = read_sql_expression2(K_FROM, K_IN,
 										   "FROM or IN",
-										   NULL);
+										   NULL, yyscanner);
 		check_FROM = false;
 	}
 	else if (tok_is_keyword(tok, &yylval,
@@ -3189,7 +3203,7 @@ read_fetch_direction(void)
 		fetch->direction = FETCH_RELATIVE;
 		fetch->expr = read_sql_expression2(K_FROM, K_IN,
 										   "FROM or IN",
-										   NULL);
+										   NULL, yyscanner);
 		check_FROM = false;
 	}
 	else if (tok_is_keyword(tok, &yylval,
@@ -3201,13 +3215,13 @@ read_fetch_direction(void)
 	else if (tok_is_keyword(tok, &yylval,
 							K_FORWARD, "forward"))
 	{
-		complete_direction(fetch, &check_FROM);
+		complete_direction(fetch, &check_FROM, yyscanner);
 	}
 	else if (tok_is_keyword(tok, &yylval,
 							K_BACKWARD, "backward"))
 	{
 		fetch->direction = FETCH_BACKWARD;
-		complete_direction(fetch, &check_FROM);
+		complete_direction(fetch, &check_FROM, yyscanner);
 	}
 	else if (tok == K_FROM || tok == K_IN)
 	{
@@ -3233,7 +3247,7 @@ read_fetch_direction(void)
 		plpgsql_push_back_token(tok);
 		fetch->expr = read_sql_expression2(K_FROM, K_IN,
 										   "FROM or IN",
-										   NULL);
+										   NULL, yyscanner);
 		fetch->returns_multiple_rows = true;
 		check_FROM = false;
 	}
@@ -3241,9 +3255,9 @@ read_fetch_direction(void)
 	/* check FROM or IN keyword after direction's specification */
 	if (check_FROM)
 	{
-		tok = yylex();
+		tok = yylex(yyscanner);
 		if (tok != K_FROM && tok != K_IN)
-			yyerror("expected FROM or IN");
+			yyerror(yyscanner, "expected FROM or IN");
 	}
 
 	return fetch;
@@ -3256,13 +3270,13 @@ read_fetch_direction(void)
  *   BACKWARD expr, BACKWARD ALL, BACKWARD
  */
 static void
-complete_direction(PLpgSQL_stmt_fetch *fetch,  bool *check_FROM)
+complete_direction(PLpgSQL_stmt_fetch *fetch,  bool *check_FROM, yyscan_t yyscanner)
 {
 	int			tok;
 
-	tok = yylex();
+	tok = yylex(yyscanner);
 	if (tok == 0)
-		yyerror("unexpected end of function definition");
+		yyerror(yyscanner, "unexpected end of function definition");
 
 	if (tok == K_FROM || tok == K_IN)
 	{
@@ -3281,14 +3295,14 @@ complete_direction(PLpgSQL_stmt_fetch *fetch,  bool *check_FROM)
 	plpgsql_push_back_token(tok);
 	fetch->expr = read_sql_expression2(K_FROM, K_IN,
 									   "FROM or IN",
-									   NULL);
+									   NULL, yyscanner);
 	fetch->returns_multiple_rows = true;
 	*check_FROM = false;
 }
 
 
 static PLpgSQL_stmt *
-make_return_stmt(int location)
+make_return_stmt(int location, yyscan_t yyscanner)
 {
 	PLpgSQL_stmt_return *new;
 
@@ -3301,7 +3315,7 @@ make_return_stmt(int location)
 
 	if (plpgsql_curr_compile->fn_retset)
 	{
-		if (yylex() != ';')
+		if (yylex(yyscanner) != ';')
 			ereport(ERROR,
 					(errcode(ERRCODE_DATATYPE_MISMATCH),
 					 errmsg("RETURN cannot have a parameter in function returning set"),
@@ -3310,7 +3324,7 @@ make_return_stmt(int location)
 	}
 	else if (plpgsql_curr_compile->fn_rettype == VOIDOID)
 	{
-		if (yylex() != ';')
+		if (yylex(yyscanner) != ';')
 		{
 			if (plpgsql_curr_compile->fn_prokind == PROKIND_PROCEDURE)
 				ereport(ERROR,
@@ -3326,7 +3340,7 @@ make_return_stmt(int location)
 	}
 	else if (plpgsql_curr_compile->out_param_varno >= 0)
 	{
-		if (yylex() != ';')
+		if (yylex(yyscanner) != ';')
 			ereport(ERROR,
 					(errcode(ERRCODE_DATATYPE_MISMATCH),
 					 errmsg("RETURN cannot have a parameter in function with OUT parameters"),
@@ -3339,9 +3353,9 @@ make_return_stmt(int location)
 		 * We want to special-case simple variable references for efficiency.
 		 * So peek ahead to see if that's what we have.
 		 */
-		int			tok = yylex();
+		int			tok = yylex(yyscanner);
 
-		if (tok == T_DATUM && plpgsql_peek() == ';' &&
+		if (tok == T_DATUM && plpgsql_peek(yyscanner) == ';' &&
 			(yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_VAR ||
 			 yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_PROMISE ||
 			 yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
@@ -3349,7 +3363,7 @@ make_return_stmt(int location)
 		{
 			new->retvarno = yylval.wdatum.datum->dno;
 			/* eat the semicolon token that we only peeked at above */
-			tok = yylex();
+			tok = yylex(yyscanner);
 			Assert(tok == ';');
 		}
 		else
@@ -3361,7 +3375,7 @@ make_return_stmt(int location)
 			 * anything else is a compile-time error.
 			 */
 			plpgsql_push_back_token(tok);
-			new->expr = read_sql_expression(';', ";");
+			new->expr = read_sql_expression(';', ";", yyscanner);
 		}
 	}
 
@@ -3370,7 +3384,7 @@ make_return_stmt(int location)
 
 
 static PLpgSQL_stmt *
-make_return_next_stmt(int location)
+make_return_next_stmt(int location, yyscan_t yyscanner)
 {
 	PLpgSQL_stmt_return_next *new;
 
@@ -3389,7 +3403,7 @@ make_return_next_stmt(int location)
 
 	if (plpgsql_curr_compile->out_param_varno >= 0)
 	{
-		if (yylex() != ';')
+		if (yylex(yyscanner) != ';')
 			ereport(ERROR,
 					(errcode(ERRCODE_DATATYPE_MISMATCH),
 					 errmsg("RETURN NEXT cannot have a parameter in function with OUT parameters"),
@@ -3402,9 +3416,9 @@ make_return_next_stmt(int location)
 		 * We want to special-case simple variable references for efficiency.
 		 * So peek ahead to see if that's what we have.
 		 */
-		int			tok = yylex();
+		int			tok = yylex(yyscanner);
 
-		if (tok == T_DATUM && plpgsql_peek() == ';' &&
+		if (tok == T_DATUM && plpgsql_peek(yyscanner) == ';' &&
 			(yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_VAR ||
 			 yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_PROMISE ||
 			 yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
@@ -3412,7 +3426,7 @@ make_return_next_stmt(int location)
 		{
 			new->retvarno = yylval.wdatum.datum->dno;
 			/* eat the semicolon token that we only peeked at above */
-			tok = yylex();
+			tok = yylex(yyscanner);
 			Assert(tok == ';');
 		}
 		else
@@ -3424,7 +3438,7 @@ make_return_next_stmt(int location)
 			 * anything else is a compile-time error.
 			 */
 			plpgsql_push_back_token(tok);
-			new->expr = read_sql_expression(';', ";");
+			new->expr = read_sql_expression(';', ";", yyscanner);
 		}
 	}
 
@@ -3433,7 +3447,7 @@ make_return_next_stmt(int location)
 
 
 static PLpgSQL_stmt *
-make_return_query_stmt(int location)
+make_return_query_stmt(int location, yyscan_t yyscanner)
 {
 	PLpgSQL_stmt_return_query *new;
 	int			tok;
@@ -3450,11 +3464,11 @@ make_return_query_stmt(int location)
 	new->stmtid = ++plpgsql_curr_compile->nstatements;
 
 	/* check for RETURN QUERY EXECUTE */
-	if ((tok = yylex()) != K_EXECUTE)
+	if ((tok = yylex(yyscanner)) != K_EXECUTE)
 	{
 		/* ordinary static query */
 		plpgsql_push_back_token(tok);
-		new->query = read_sql_stmt();
+		new->query = read_sql_stmt(yyscanner);
 	}
 	else
 	{
@@ -3462,14 +3476,14 @@ make_return_query_stmt(int location)
 		int			term;
 
 		new->dynquery = read_sql_expression2(';', K_USING, "; or USING",
-											 &term);
+											 &term, yyscanner);
 		if (term == K_USING)
 		{
 			do
 			{
 				PLpgSQL_expr *expr;
 
-				expr = read_sql_expression2(',', ';', ", or ;", &term);
+				expr = read_sql_expression2(',', ';', ", or ;", &term, yyscanner);
 				new->params = lappend(new->params, expr);
 			} while (term == ',');
 		}
@@ -3523,7 +3537,7 @@ check_assignable(PLpgSQL_datum *datum, int location)
  * INTO keyword.
  */
 static void
-read_into_target(PLpgSQL_variable **target, bool *strict)
+read_into_target(PLpgSQL_variable **target, bool *strict, yyscan_t yyscanner)
 {
 	int			tok;
 
@@ -3532,11 +3546,11 @@ read_into_target(PLpgSQL_variable **target, bool *strict)
 	if (strict)
 		*strict = false;
 
-	tok = yylex();
+	tok = yylex(yyscanner);
 	if (strict && tok == K_STRICT)
 	{
 		*strict = true;
-		tok = yylex();
+		tok = yylex(yyscanner);
 	}
 
 	/*
@@ -3555,7 +3569,7 @@ read_into_target(PLpgSQL_variable **target, bool *strict)
 				check_assignable(yylval.wdatum.datum, yylloc);
 				*target = (PLpgSQL_variable *) yylval.wdatum.datum;
 
-				if ((tok = yylex()) == ',')
+				if ((tok = yylex(yyscanner)) == ',')
 					ereport(ERROR,
 							(errcode(ERRCODE_SYNTAX_ERROR),
 							 errmsg("record variable cannot be part of multiple-item INTO list"),
@@ -3566,13 +3580,13 @@ read_into_target(PLpgSQL_variable **target, bool *strict)
 			{
 				*target = (PLpgSQL_variable *)
 					read_into_scalar_list(NameOfDatum(&(yylval.wdatum)),
-										  yylval.wdatum.datum, yylloc);
+										  yylval.wdatum.datum, yylloc, yyscanner);
 			}
 			break;
 
 		default:
 			/* just to give a better message than "syntax error" */
-			current_token_is_not_variable(tok);
+			current_token_is_not_variable(tok, yyscanner);
 	}
 }
 
@@ -3585,7 +3599,8 @@ read_into_target(PLpgSQL_variable **target, bool *strict)
 static PLpgSQL_row *
 read_into_scalar_list(char *initial_name,
 					  PLpgSQL_datum *initial_datum,
-					  int initial_location)
+					  int initial_location,
+					  yyscan_t yyscanner)
 {
 	int			nfields;
 	char	   *fieldnames[1024];
@@ -3598,7 +3613,7 @@ read_into_scalar_list(char *initial_name,
 	varnos[0] = initial_datum->dno;
 	nfields = 1;
 
-	while ((tok = yylex()) == ',')
+	while ((tok = yylex(yyscanner)) == ',')
 	{
 		/* Check for array overflow */
 		if (nfields >= 1024)
@@ -3607,7 +3622,7 @@ read_into_scalar_list(char *initial_name,
 					 errmsg("too many INTO variables specified"),
 					 parser_errposition(yylloc)));
 
-		tok = yylex();
+		tok = yylex(yyscanner);
 		switch (tok)
 		{
 			case T_DATUM:
@@ -3625,7 +3640,7 @@ read_into_scalar_list(char *initial_name,
 
 			default:
 				/* just to give a better message than "syntax error" */
-				current_token_is_not_variable(tok);
+				current_token_is_not_variable(tok, yyscanner);
 		}
 	}
 
@@ -3836,7 +3851,7 @@ check_labels(const char *start_label, const char *end_label, int end_location)
  * parens).
  */
 static PLpgSQL_expr *
-read_cursor_args(PLpgSQL_var *cursor, int until)
+read_cursor_args(PLpgSQL_var *cursor, int until, yyscan_t yyscanner)
 {
 	PLpgSQL_expr *expr;
 	PLpgSQL_row *row;
@@ -3846,7 +3861,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
 	StringInfoData ds;
 	bool		any_named = false;
 
-	tok = yylex();
+	tok = yylex(yyscanner);
 	if (cursor->cursor_explicit_argrow < 0)
 	{
 		/* No arguments expected */
@@ -3858,7 +3873,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
 					 parser_errposition(yylloc)));
 
 		if (tok != until)
-			yyerror("syntax error");
+			yyerror(yyscanner, "syntax error");
 
 		return NULL;
 	}
@@ -3887,7 +3902,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
 		int			arglocation;
 
 		/* Check if it's a named parameter: "param := value" */
-		plpgsql_peek2(&tok1, &tok2, &arglocation, NULL);
+		plpgsql_peek2(&tok1, &tok2, &arglocation, NULL, yyscanner);
 		if (tok1 == IDENT && tok2 == COLON_EQUALS)
 		{
 			char   *argname;
@@ -3896,7 +3911,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
 			/* Read the argument name, ignoring any matching variable */
 			save_IdentifierLookup = plpgsql_IdentifierLookup;
 			plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_DECLARE;
-			yylex();
+			yylex(yyscanner);
 			argname = yylval.str;
 			plpgsql_IdentifierLookup = save_IdentifierLookup;
 
@@ -3917,9 +3932,9 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
 			 * Eat the ":=". We already peeked, so the error should never
 			 * happen.
 			 */
-			tok2 = yylex();
+			tok2 = yylex(yyscanner);
 			if (tok2 != COLON_EQUALS)
-				yyerror("syntax error");
+				yyerror(yyscanner, "syntax error");
 
 			any_named = true;
 		}
@@ -3943,7 +3958,8 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
 								  ",\" or \")",
 								  RAW_PARSE_PLPGSQL_EXPR,
 								  true, true,
-								  NULL, &endtoken);
+								  NULL, &endtoken,
+								  yyscanner);
 
 		argv[argpos] = item->query;
 
@@ -3990,9 +4006,9 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
 	pfree(ds.data);
 
 	/* Next we'd better find the until token */
-	tok = yylex();
+	tok = yylex(yyscanner);
 	if (tok != until)
-		yyerror("syntax error");
+		yyerror(yyscanner, "syntax error");
 
 	return expr;
 }
@@ -4001,7 +4017,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
  * Parse RAISE ... USING options
  */
 static List *
-read_raise_options(void)
+read_raise_options(yyscan_t yyscanner)
 {
 	List	   *result = NIL;
 
@@ -4010,8 +4026,8 @@ read_raise_options(void)
 		PLpgSQL_raise_option *opt;
 		int			tok;
 
-		if ((tok = yylex()) == 0)
-			yyerror("unexpected end of function definition");
+		if ((tok = yylex(yyscanner)) == 0)
+			yyerror(yyscanner, "unexpected end of function definition");
 
 		opt = (PLpgSQL_raise_option *) palloc(sizeof(PLpgSQL_raise_option));
 
@@ -4043,13 +4059,13 @@ read_raise_options(void)
 								K_SCHEMA, "schema"))
 			opt->opt_type = PLPGSQL_RAISEOPTION_SCHEMA;
 		else
-			yyerror("unrecognized RAISE statement option");
+			yyerror(yyscanner, "unrecognized RAISE statement option");
 
-		tok = yylex();
+		tok = yylex(yyscanner);
 		if (tok != '=' && tok != COLON_EQUALS)
-			yyerror("syntax error, expected \"=\"");
+			yyerror(yyscanner, "syntax error, expected \"=\"");
 
-		opt->expr = read_sql_expression2(',', ';', ", or ;", &tok);
+		opt->expr = read_sql_expression2(',', ';', ", or ;", &tok, yyscanner);
 
 		result = lappend(result, opt);
 
diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c
index 9407da51efa..e24b107909b 100644
--- a/src/pl/plpgsql/src/pl_scanner.c
+++ b/src/pl/plpgsql/src/pl_scanner.c
@@ -103,7 +103,6 @@ typedef struct
  */
 
 /* The stuff the core lexer needs */
-static core_yyscan_t yyscanner = NULL;
 static core_yy_extra_type core_yy;
 
 /* The original input string */
@@ -128,7 +127,7 @@ static const char *cur_line_end;
 static int	cur_line_num;
 
 /* Internal functions */
-static int	internal_yylex(TokenAuxData *auxdata);
+static int	internal_yylex(TokenAuxData *auxdata, yyscan_t yyscanner);
 static void push_back_token(int token, TokenAuxData *auxdata);
 static void location_lineno_init(void);
 
@@ -143,37 +142,37 @@ static void location_lineno_init(void);
  * matches one of those.
  */
 int
-plpgsql_yylex(void)
+plpgsql_yylex(yyscan_t yyscanner)
 {
 	int			tok1;
 	TokenAuxData aux1;
 	int			kwnum;
 
-	tok1 = internal_yylex(&aux1);
+	tok1 = internal_yylex(&aux1, yyscanner);
 	if (tok1 == IDENT || tok1 == PARAM)
 	{
 		int			tok2;
 		TokenAuxData aux2;
 
-		tok2 = internal_yylex(&aux2);
+		tok2 = internal_yylex(&aux2, yyscanner);
 		if (tok2 == '.')
 		{
 			int			tok3;
 			TokenAuxData aux3;
 
-			tok3 = internal_yylex(&aux3);
+			tok3 = internal_yylex(&aux3, yyscanner);
 			if (tok3 == IDENT)
 			{
 				int			tok4;
 				TokenAuxData aux4;
 
-				tok4 = internal_yylex(&aux4);
+				tok4 = internal_yylex(&aux4, yyscanner);
 				if (tok4 == '.')
 				{
 					int			tok5;
 					TokenAuxData aux5;
 
-					tok5 = internal_yylex(&aux5);
+					tok5 = internal_yylex(&aux5, yyscanner);
 					if (tok5 == IDENT)
 					{
 						if (plpgsql_parse_tripword(aux1.lval.str,
@@ -322,7 +321,7 @@ plpgsql_token_length(void)
  * interfacing from the core_YYSTYPE to YYSTYPE union.
  */
 static int
-internal_yylex(TokenAuxData *auxdata)
+internal_yylex(TokenAuxData *auxdata, yyscan_t yyscanner)
 {
 	int			token;
 	const char *yytext;
@@ -434,12 +433,12 @@ plpgsql_append_source_text(StringInfo buf,
  * be returned as IDENT. Reserved keywords are resolved as usual.
  */
 int
-plpgsql_peek(void)
+plpgsql_peek(yyscan_t yyscanner)
 {
 	int			tok1;
 	TokenAuxData aux1;
 
-	tok1 = internal_yylex(&aux1);
+	tok1 = internal_yylex(&aux1, yyscanner);
 	push_back_token(tok1, &aux1);
 	return tok1;
 }
@@ -453,15 +452,15 @@ plpgsql_peek(void)
  * be returned as IDENT. Reserved keywords are resolved as usual.
  */
 void
-plpgsql_peek2(int *tok1_p, int *tok2_p, int *tok1_loc, int *tok2_loc)
+plpgsql_peek2(int *tok1_p, int *tok2_p, int *tok1_loc, int *tok2_loc, yyscan_t yyscanner)
 {
 	int			tok1,
 				tok2;
 	TokenAuxData aux1,
 				aux2;
 
-	tok1 = internal_yylex(&aux1);
-	tok2 = internal_yylex(&aux2);
+	tok1 = internal_yylex(&aux1, yyscanner);
+	tok2 = internal_yylex(&aux2, yyscanner);
 
 	*tok1_p = tok1;
 	if (tok1_loc)
@@ -513,7 +512,7 @@ plpgsql_scanner_errposition(int location)
  * be misleading!
  */
 void
-plpgsql_yyerror(const char *message)
+plpgsql_yyerror(yyscan_t yyscanner, const char *message)
 {
 	char	   *yytext = core_yy.scanbuf + plpgsql_yylloc;
 
@@ -599,9 +598,11 @@ plpgsql_latest_lineno(void)
  * Although it is not fed directly to flex, we need the original string
  * to cite in error messages.
  */
-void
+yyscan_t
 plpgsql_scanner_init(const char *str)
 {
+	yyscan_t	yyscanner;
+
 	/* Start up the core scanner */
 	yyscanner = scanner_init(str, &core_yy,
 							 &ReservedPLKeywords, ReservedPLKeywordTokens);
@@ -621,17 +622,18 @@ plpgsql_scanner_init(const char *str)
 	num_pushbacks = 0;
 
 	location_lineno_init();
+
+	return yyscanner;
 }
 
 /*
  * Called after parsing is done to clean up after plpgsql_scanner_init()
  */
 void
-plpgsql_scanner_finish(void)
+plpgsql_scanner_finish(yyscan_t yyscanner)
 {
 	/* release storage */
 	scanner_finish(yyscanner);
 	/* avoid leaving any dangling pointers */
-	yyscanner = NULL;
 	scanorig = NULL;
 }
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 50c3b28472b..dab4e16c72e 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -1315,26 +1315,26 @@ extern void plpgsql_dumptree(PLpgSQL_function *func);
 /*
  * Scanner functions in pl_scanner.c
  */
-extern int	plpgsql_base_yylex(void);
-extern int	plpgsql_yylex(void);
+typedef void *yyscan_t;
+extern int	plpgsql_yylex(yyscan_t yyscanner);
 extern int	plpgsql_token_length(void);
 extern void plpgsql_push_back_token(int token);
 extern bool plpgsql_token_is_unreserved_keyword(int token);
 extern void plpgsql_append_source_text(StringInfo buf,
 									   int startlocation, int endlocation);
-extern int	plpgsql_peek(void);
+extern int	plpgsql_peek(yyscan_t yyscanner);
 extern void plpgsql_peek2(int *tok1_p, int *tok2_p, int *tok1_loc,
-						  int *tok2_loc);
+						  int *tok2_loc, yyscan_t yyscanner);
 extern int	plpgsql_scanner_errposition(int location);
-extern void plpgsql_yyerror(const char *message) pg_attribute_noreturn();
+extern void plpgsql_yyerror(yyscan_t yyscanner, const char *message) pg_attribute_noreturn();
 extern int	plpgsql_location_to_lineno(int location);
 extern int	plpgsql_latest_lineno(void);
-extern void plpgsql_scanner_init(const char *str);
-extern void plpgsql_scanner_finish(void);
+extern yyscan_t plpgsql_scanner_init(const char *str);
+extern void plpgsql_scanner_finish(yyscan_t yyscanner);
 
 /*
  * Externs in gram.y
  */
-extern int	plpgsql_yyparse(void);
+extern int	plpgsql_yyparse(yyscan_t yyscanner);
 
 #endif							/* PLPGSQL_H */
-- 
2.47.1