v3-0003-plpgsql-pure-parser.patch

text/plain

Filename: v3-0003-plpgsql-pure-parser.patch
Type: text/plain
Part: 2
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 v3-0003
Subject: plpgsql: pure parser
File+
src/pl/plpgsql/src/nls.mk 1 1
src/pl/plpgsql/src/pl_gram.y 302 300
src/pl/plpgsql/src/plpgsql.h 5 3
src/pl/plpgsql/src/pl_scanner.c 10 10
From 5821e9ce7ff85f7304c56f8ff0483f2e07b3be8e Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Thu, 26 Dec 2024 00:08:25 +0100
Subject: [PATCH v3 3/4] plpgsql: pure parser

---
 src/pl/plpgsql/src/nls.mk       |   2 +-
 src/pl/plpgsql/src/pl_gram.y    | 602 ++++++++++++++++----------------
 src/pl/plpgsql/src/pl_scanner.c |  20 +-
 src/pl/plpgsql/src/plpgsql.h    |   8 +-
 4 files changed, 318 insertions(+), 314 deletions(-)

diff --git a/src/pl/plpgsql/src/nls.mk b/src/pl/plpgsql/src/nls.mk
index ec7c7035add..eb06336675c 100644
--- a/src/pl/plpgsql/src/nls.mk
+++ b/src/pl/plpgsql/src/nls.mk
@@ -6,5 +6,5 @@ GETTEXT_FILES    = pl_comp.c \
                    pl_funcs.c \
                    pl_handler.c \
                    pl_scanner.c
-GETTEXT_TRIGGERS = $(BACKEND_COMMON_GETTEXT_TRIGGERS) yyerror:2 plpgsql_yyerror:2
+GETTEXT_TRIGGERS = $(BACKEND_COMMON_GETTEXT_TRIGGERS) yyerror:3 plpgsql_yyerror:3
 GETTEXT_FLAGS    = $(BACKEND_COMMON_GETTEXT_FLAGS)
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index e8f81d287a1..cb5c2dca186 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -28,10 +28,6 @@
 
 #include "pl_gram.h"
 
-/* silence -Wmissing-variable-declarations */
-extern int plpgsql_yychar;
-extern int plpgsql_yynerrs;
-
 /* Location tracking support --- simpler than bison's default */
 #define YYLLOC_DEFAULT(Current, Rhs, N) \
 	do { \
@@ -63,7 +59,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, yyscan_t yyscanner);
+static	void			current_token_is_not_variable(int tok, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
 static	PLpgSQL_expr	*read_sql_construct(int until,
 											int until2,
 											int until3,
@@ -73,31 +69,33 @@ static	PLpgSQL_expr	*read_sql_construct(int until,
 											bool valid_sql,
 											int *startloc,
 											int *endtoken,
+											YYSTYPE *yylvalp, YYLTYPE *yyllocp,
 											yyscan_t yyscanner);
-static	PLpgSQL_expr	*read_sql_expression(int until,
-											 const char *expected, yyscan_t yyscanner);
+static	PLpgSQL_expr	*read_sql_expression(int until, const char *expected,
+											 YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
 static	PLpgSQL_expr	*read_sql_expression2(int until, int until2,
-											  const char *expected,
-											  int *endtoken, yyscan_t yyscanner);
-static	PLpgSQL_expr	*read_sql_stmt(yyscan_t yyscanner);
-static	PLpgSQL_type	*read_datatype(int tok, yyscan_t yyscanner);
+											  const char *expected, int *endtoken,
+											  YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
+static	PLpgSQL_expr	*read_sql_stmt(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
+static	PLpgSQL_type	*read_datatype(int tok, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
 static	PLpgSQL_stmt	*make_execsql_stmt(int firsttoken, int location,
-										   PLword *word, yyscan_t yyscanner);
-static	PLpgSQL_stmt_fetch *read_fetch_direction(yyscan_t yyscanner);
+										   PLword *word, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
+static	PLpgSQL_stmt_fetch *read_fetch_direction(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
 static	void			 complete_direction(PLpgSQL_stmt_fetch *fetch,
-											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);
+											bool *check_FROM, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
+static	PLpgSQL_stmt	*make_return_stmt(int location, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
+static	PLpgSQL_stmt	*make_return_next_stmt(int location, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
+static	PLpgSQL_stmt	*make_return_query_stmt(int location, YYSTYPE *yylvalp, YYLTYPE *yyllocp, 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, yyscan_t yyscanner);
+static	void			 read_into_target(PLpgSQL_variable **target, bool *strict,
+										  YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
 static	PLpgSQL_row		*read_into_scalar_list(char *initial_name,
 											   PLpgSQL_datum *initial_datum,
 											   int initial_location,
+											   YYSTYPE *yylvalp, YYLTYPE *yyllocp,
 											   yyscan_t yyscanner);
 static	PLpgSQL_row		*make_scalar_list1(char *initial_name,
 										   PLpgSQL_datum *initial_datum,
@@ -109,15 +107,16 @@ static	PLpgSQL_type	*parse_datatype(const char *string, int location);
 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, yyscan_t yyscanner);
-static	List			*read_raise_options(yyscan_t yyscanner);
+static	PLpgSQL_expr	*read_cursor_args(PLpgSQL_var *cursor, int until,
+										  YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
+static	List			*read_raise_options(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
 static	void			check_raise_parameters(PLpgSQL_stmt_raise *stmt);
 
 %}
 
 %parse-param {yyscan_t yyscanner}
 %lex-param   {yyscan_t yyscanner}
+%pure-parser
 %expect 0
 %name-prefix="plpgsql_yy"
 %locations
@@ -581,7 +580,7 @@ opt_scrollable :
 
 decl_cursor_query :
 					{
-						$$ = read_sql_stmt(yyscanner);
+						$$ = read_sql_stmt(&yylval, &yylloc, yyscanner);
 					}
 				;
 
@@ -710,7 +709,7 @@ decl_varname	: T_WORD
 						if (plpgsql_ns_lookup(plpgsql_ns_top(), true,
 											  $1.ident, NULL, NULL,
 											  NULL) != NULL)
-							yyerror(yyscanner, "duplicate declaration");
+							yyerror(&yylloc, yyscanner, "duplicate declaration");
 
 						if (plpgsql_curr_compile->extra_warnings & PLPGSQL_XCHECK_SHADOWVAR ||
 							plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_SHADOWVAR)
@@ -738,7 +737,7 @@ decl_varname	: T_WORD
 						if (plpgsql_ns_lookup(plpgsql_ns_top(), true,
 											  $1, NULL, NULL,
 											  NULL) != NULL)
-							yyerror(yyscanner, "duplicate declaration");
+							yyerror(&yylloc, yyscanner, "duplicate declaration");
 
 						if (plpgsql_curr_compile->extra_warnings & PLPGSQL_XCHECK_SHADOWVAR ||
 							plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_SHADOWVAR)
@@ -770,7 +769,7 @@ decl_datatype	:
 						 * consume it, and then we must tell bison to forget
 						 * it.
 						 */
-						$$ = read_datatype(yychar, yyscanner);
+						$$ = read_datatype(yychar, &yylval, &yylloc, yyscanner);
 						yyclearin;
 					}
 				;
@@ -803,7 +802,7 @@ decl_defval		: ';'
 					{ $$ = NULL; }
 				| decl_defkey
 					{
-						$$ = read_sql_expression(';', ";", yyscanner);
+						$$ = read_sql_expression(';', ";", &yylval, &yylloc, yyscanner);
 					}
 				;
 
@@ -891,7 +890,7 @@ stmt_perform	: K_PERFORM
 						new->cmd_type = PLPGSQL_STMT_PERFORM;
 						new->lineno   = plpgsql_location_to_lineno(@1);
 						new->stmtid = ++plpgsql_curr_compile->nstatements;
-						plpgsql_push_back_token(K_PERFORM);
+						plpgsql_push_back_token(K_PERFORM, &yylval, &yylloc);
 
 						/*
 						 * Since PERFORM isn't legal SQL, we have to cheat to
@@ -905,7 +904,7 @@ stmt_perform	: K_PERFORM
 													   RAW_PARSE_DEFAULT,
 													   false, false,
 													   &startloc, NULL,
-													   yyscanner);
+													   &yylval, &yylloc, yyscanner);
 						/* overwrite "perform" ... */
 						memcpy(new->expr->query, " SELECT", 7);
 						/* left-justify to get rid of the leading space */
@@ -927,8 +926,8 @@ stmt_call		: K_CALL
 						new->cmd_type = PLPGSQL_STMT_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(yyscanner);
+						plpgsql_push_back_token(K_CALL, &yylval, &yylloc);
+						new->expr = read_sql_stmt(&yylval, &yylloc, yyscanner);
 						new->is_call = true;
 
 						/* Remember we may need a procedure resource owner */
@@ -946,8 +945,8 @@ stmt_call		: K_CALL
 						new->cmd_type = PLPGSQL_STMT_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(yyscanner);
+						plpgsql_push_back_token(K_DO, &yylval, &yylloc);
+						new->expr = read_sql_stmt(&yylval, &yylloc, yyscanner);
 						new->is_call = false;
 
 						/* Remember we may need a procedure resource owner */
@@ -987,12 +986,12 @@ stmt_assign		: T_DATUM
 						new->stmtid = ++plpgsql_curr_compile->nstatements;
 						new->varno = $1.datum->dno;
 						/* Push back the head name to include it in the stmt */
-						plpgsql_push_back_token(T_DATUM);
+						plpgsql_push_back_token(T_DATUM, &yylval, &yylloc);
 						new->expr = read_sql_construct(';', 0, 0, ";",
 													   pmode,
 													   false, true,
 													   NULL, NULL,
-													   yyscanner);
+													   &yylval, &yylloc, yyscanner);
 
 						$$ = (PLpgSQL_stmt *) new;
 					}
@@ -1099,7 +1098,7 @@ getdiag_list_item : getdiag_target assign_operator getdiag_item
 
 getdiag_item :
 					{
-						int			tok = yylex(yyscanner);
+						int			tok = yylex(&yylval, &yylloc, yyscanner);
 
 						if (tok_is_keyword(tok, &yylval,
 										   K_ROW_COUNT, "row_count"))
@@ -1141,7 +1140,7 @@ getdiag_item :
 												K_RETURNED_SQLSTATE, "returned_sqlstate"))
 							$$ = PLPGSQL_GETDIAG_RETURNED_SQLSTATE;
 						else
-							yyerror(yyscanner, "unrecognized GET DIAGNOSTICS item");
+							yyerror(&yylloc, yyscanner, "unrecognized GET DIAGNOSTICS item");
 					}
 				;
 
@@ -1228,14 +1227,14 @@ 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(yyscanner);
+						int			tok = yylex(&yylval, &yylloc, yyscanner);
 
 						if (tok != K_WHEN)
 						{
-							plpgsql_push_back_token(tok);
-							expr = read_sql_expression(K_WHEN, "WHEN", yyscanner);
+							plpgsql_push_back_token(tok, &yylval, &yylloc);
+							expr = read_sql_expression(K_WHEN, "WHEN", &yylval, &yylloc, yyscanner);
 						}
-						plpgsql_push_back_token(K_WHEN);
+						plpgsql_push_back_token(K_WHEN, &yylval, &yylloc);
 						$$ = expr;
 					}
 				;
@@ -1353,7 +1352,7 @@ stmt_for		: opt_loop_label K_FOR for_control loop_body
 
 for_control		: for_variable K_IN
 					{
-						int			tok = yylex(yyscanner);
+						int			tok = yylex(&yylval, &yylloc, yyscanner);
 						int			tokloc = yylloc;
 
 						if (tok == K_EXECUTE)
@@ -1365,7 +1364,7 @@ for_control		: for_variable K_IN
 
 							expr = read_sql_expression2(K_LOOP, K_USING,
 														"LOOP or USING",
-														&term, yyscanner);
+														&term, &yylval, &yylloc, yyscanner);
 
 							new = palloc0(sizeof(PLpgSQL_stmt_dynfors));
 							new->cmd_type = PLPGSQL_STMT_DYNFORS;
@@ -1398,7 +1397,7 @@ for_control		: for_variable K_IN
 								{
 									expr = read_sql_expression2(',', K_LOOP,
 																", or LOOP",
-																&term, yyscanner);
+																&term, &yylval, &yylloc, yyscanner);
 									new->params = lappend(new->params, expr);
 								} while (term == ',');
 							}
@@ -1433,7 +1432,7 @@ for_control		: for_variable K_IN
 										 parser_errposition(tokloc)));
 
 							/* collect cursor's parameters if any */
-							new->argquery = read_cursor_args(cursor, K_LOOP, yyscanner);
+							new->argquery = read_cursor_args(cursor, K_LOOP, &yylval, &yylloc, yyscanner);
 
 							/* create loop's private RECORD variable */
 							new->var = (PLpgSQL_variable *)
@@ -1467,7 +1466,7 @@ for_control		: for_variable K_IN
 											   K_REVERSE, "reverse"))
 								reverse = true;
 							else
-								plpgsql_push_back_token(tok);
+								plpgsql_push_back_token(tok, &yylval, &yylloc);
 
 							/*
 							 * Read tokens until we see either a ".."
@@ -1485,7 +1484,7 @@ for_control		: for_variable K_IN
 													   false,
 													   &expr1loc,
 													   &tok,
-													   yyscanner);
+													   &yylval, &yylloc, yyscanner);
 
 							if (tok == DOT_DOT)
 							{
@@ -1506,12 +1505,12 @@ for_control		: for_variable K_IN
 								/* Read and check the second one */
 								expr2 = read_sql_expression2(K_LOOP, K_BY,
 															 "LOOP",
-															 &tok, yyscanner);
+															 &tok, &yylval, &yylloc, yyscanner);
 
 								/* Get the BY clause if any */
 								if (tok == K_BY)
 									expr_by = read_sql_expression(K_LOOP,
-																  "LOOP", yyscanner);
+																  "LOOP", &yylval, &yylloc, yyscanner);
 								else
 									expr_by = NULL;
 
@@ -1626,13 +1625,14 @@ for_variable	: T_DATUM
 							$$.scalar = $1.datum;
 							$$.row = NULL;
 							/* check for comma-separated list */
-							tok = yylex(yyscanner);
-							plpgsql_push_back_token(tok);
+							tok = yylex(&yylval, &yylloc, yyscanner);
+							plpgsql_push_back_token(tok, &yylval, &yylloc);
 							if (tok == ',')
 								$$.row = (PLpgSQL_datum *)
 									read_into_scalar_list($$.name,
 														  $$.scalar,
 														  @1,
+														  &yylval, &yylloc,
 														  yyscanner);
 						}
 					}
@@ -1645,8 +1645,8 @@ for_variable	: T_DATUM
 						$$.scalar = NULL;
 						$$.row = NULL;
 						/* check for comma-separated list */
-						tok = yylex(yyscanner);
-						plpgsql_push_back_token(tok);
+						tok = yylex(&yylval, &yylloc, yyscanner);
+						plpgsql_push_back_token(tok, &yylval, &yylloc);
 						if (tok == ',')
 							word_is_not_variable(&($1), @1);
 					}
@@ -1772,24 +1772,24 @@ stmt_return		: K_RETURN
 					{
 						int			tok;
 
-						tok = yylex(yyscanner);
+						tok = yylex(&yylval, &yylloc, yyscanner);
 						if (tok == 0)
-							yyerror(yyscanner, "unexpected end of function definition");
+							yyerror(&yylloc, yyscanner, "unexpected end of function definition");
 
 						if (tok_is_keyword(tok, &yylval,
 										   K_NEXT, "next"))
 						{
-							$$ = make_return_next_stmt(@1, yyscanner);
+							$$ = make_return_next_stmt(@1, &yylval, &yylloc, yyscanner);
 						}
 						else if (tok_is_keyword(tok, &yylval,
 												K_QUERY, "query"))
 						{
-							$$ = make_return_query_stmt(@1, yyscanner);
+							$$ = make_return_query_stmt(@1, &yylval, &yylloc, yyscanner);
 						}
 						else
 						{
-							plpgsql_push_back_token(tok);
-							$$ = make_return_stmt(@1, yyscanner);
+							plpgsql_push_back_token(tok, &yylval, &yylloc);
+							$$ = make_return_stmt(@1, &yylval, &yylloc, yyscanner);
 						}
 					}
 				;
@@ -1810,9 +1810,9 @@ stmt_raise		: K_RAISE
 						new->params = NIL;
 						new->options = NIL;
 
-						tok = yylex(yyscanner);
+						tok = yylex(&yylval, &yylloc, yyscanner);
 						if (tok == 0)
-							yyerror(yyscanner, "unexpected end of function definition");
+							yyerror(&yylloc, yyscanner, "unexpected end of function definition");
 
 						/*
 						 * We could have just RAISE, meaning to re-throw
@@ -1827,40 +1827,40 @@ stmt_raise		: K_RAISE
 											   K_EXCEPTION, "exception"))
 							{
 								new->elog_level = ERROR;
-								tok = yylex(yyscanner);
+								tok = yylex(&yylval, &yylloc, yyscanner);
 							}
 							else if (tok_is_keyword(tok, &yylval,
 													K_WARNING, "warning"))
 							{
 								new->elog_level = WARNING;
-								tok = yylex(yyscanner);
+								tok = yylex(&yylval, &yylloc, yyscanner);
 							}
 							else if (tok_is_keyword(tok, &yylval,
 													K_NOTICE, "notice"))
 							{
 								new->elog_level = NOTICE;
-								tok = yylex(yyscanner);
+								tok = yylex(&yylval, &yylloc, yyscanner);
 							}
 							else if (tok_is_keyword(tok, &yylval,
 													K_INFO, "info"))
 							{
 								new->elog_level = INFO;
-								tok = yylex(yyscanner);
+								tok = yylex(&yylval, &yylloc, yyscanner);
 							}
 							else if (tok_is_keyword(tok, &yylval,
 													K_LOG, "log"))
 							{
 								new->elog_level = LOG;
-								tok = yylex(yyscanner);
+								tok = yylex(&yylval, &yylloc, yyscanner);
 							}
 							else if (tok_is_keyword(tok, &yylval,
 													K_DEBUG, "debug"))
 							{
 								new->elog_level = DEBUG1;
-								tok = yylex(yyscanner);
+								tok = yylex(&yylval, &yylloc, yyscanner);
 							}
 							if (tok == 0)
-								yyerror(yyscanner, "unexpected end of function definition");
+								yyerror(&yylloc, yyscanner, "unexpected end of function definition");
 
 							/*
 							 * Next we can have a condition name, or
@@ -1878,9 +1878,9 @@ stmt_raise		: K_RAISE
 								 * begins the list of parameter expressions,
 								 * or USING to begin the options list.
 								 */
-								tok = yylex(yyscanner);
+								tok = yylex(&yylval, &yylloc, yyscanner);
 								if (tok != ',' && tok != ';' && tok != K_USING)
-									yyerror(yyscanner, "syntax error");
+									yyerror(&yylloc, yyscanner, "syntax error");
 
 								while (tok == ',')
 								{
@@ -1891,7 +1891,7 @@ stmt_raise		: K_RAISE
 															  RAW_PARSE_PLPGSQL_EXPR,
 															  true, true,
 															  NULL, &tok,
-															  yyscanner);
+															  &yylval, &yylloc, yyscanner);
 									new->params = lappend(new->params, expr);
 								}
 							}
@@ -1904,14 +1904,14 @@ stmt_raise		: K_RAISE
 									/* next token should be a string literal */
 									char	   *sqlstatestr;
 
-									if (yylex(yyscanner) != SCONST)
-										yyerror(yyscanner, "syntax error");
+									if (yylex(&yylval, &yylloc, yyscanner) != SCONST)
+										yyerror(&yylloc, yyscanner, "syntax error");
 									sqlstatestr = yylval.str;
 
 									if (strlen(sqlstatestr) != 5)
-										yyerror(yyscanner, "invalid SQLSTATE code");
+										yyerror(&yylloc, yyscanner, "invalid SQLSTATE code");
 									if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5)
-										yyerror(yyscanner, "invalid SQLSTATE code");
+										yyerror(&yylloc, yyscanner, "invalid SQLSTATE code");
 									new->condname = sqlstatestr;
 								}
 								else
@@ -1921,17 +1921,17 @@ stmt_raise		: K_RAISE
 									else if (plpgsql_token_is_unreserved_keyword(tok))
 										new->condname = pstrdup(yylval.keyword);
 									else
-										yyerror(yyscanner, "syntax error");
+										yyerror(&yylloc, yyscanner, "syntax error");
 									plpgsql_recognize_err_condition(new->condname,
 																	false);
 								}
-								tok = yylex(yyscanner);
+								tok = yylex(&yylval, &yylloc, yyscanner);
 								if (tok != ';' && tok != K_USING)
-									yyerror(yyscanner, "syntax error");
+									yyerror(&yylloc, yyscanner, "syntax error");
 							}
 
 							if (tok == K_USING)
-								new->options = read_raise_options(yyscanner);
+								new->options = read_raise_options(&yylval, &yylloc, yyscanner);
 						}
 
 						check_raise_parameters(new);
@@ -1953,10 +1953,10 @@ stmt_assert		: K_ASSERT
 
 						new->cond = read_sql_expression2(',', ';',
 														 ", or ;",
-														 &tok, yyscanner);
+														 &tok, &yylval, &yylloc, yyscanner);
 
 						if (tok == ',')
-							new->message = read_sql_expression(';', ";", yyscanner);
+							new->message = read_sql_expression(';', ";", &yylval, &yylloc, yyscanner);
 						else
 							new->message = NULL;
 
@@ -1984,37 +1984,37 @@ loop_body		: proc_sect K_END K_LOOP opt_label ';'
  */
 stmt_execsql	: K_IMPORT
 					{
-						$$ = make_execsql_stmt(K_IMPORT, @1, NULL, yyscanner);
+						$$ = make_execsql_stmt(K_IMPORT, @1, NULL, &yylval, &yylloc, yyscanner);
 					}
 				| K_INSERT
 					{
-						$$ = make_execsql_stmt(K_INSERT, @1, NULL, yyscanner);
+						$$ = make_execsql_stmt(K_INSERT, @1, NULL, &yylval, &yylloc, yyscanner);
 					}
 				| K_MERGE
 					{
-						$$ = make_execsql_stmt(K_MERGE, @1, NULL, yyscanner);
+						$$ = make_execsql_stmt(K_MERGE, @1, NULL, &yylval, &yylloc, yyscanner);
 					}
 				| T_WORD
 					{
 						int			tok;
 
-						tok = yylex(yyscanner);
-						plpgsql_push_back_token(tok);
+						tok = yylex(&yylval, &yylloc, yyscanner);
+						plpgsql_push_back_token(tok, &yylval, &yylloc);
 						if (tok == '=' || tok == COLON_EQUALS ||
 							tok == '[' || tok == '.')
 							word_is_not_variable(&($1), @1);
-						$$ = make_execsql_stmt(T_WORD, @1, &($1), yyscanner);
+						$$ = make_execsql_stmt(T_WORD, @1, &($1), &yylval, &yylloc, yyscanner);
 					}
 				| T_CWORD
 					{
 						int			tok;
 
-						tok = yylex(yyscanner);
-						plpgsql_push_back_token(tok);
+						tok = yylex(&yylval, &yylloc, yyscanner);
+						plpgsql_push_back_token(tok, &yylval, &yylloc);
 						if (tok == '=' || tok == COLON_EQUALS ||
 							tok == '[' || tok == '.')
 							cword_is_not_variable(&($1), @1);
-						$$ = make_execsql_stmt(T_CWORD, @1, NULL, yyscanner);
+						$$ = make_execsql_stmt(T_CWORD, @1, NULL, &yylval, &yylloc, yyscanner);
 					}
 				;
 
@@ -2029,7 +2029,7 @@ stmt_dynexecute : K_EXECUTE
 												  RAW_PARSE_PLPGSQL_EXPR,
 												  true, true,
 												  NULL, &endtoken,
-												  yyscanner);
+												  &yylval, &yylloc, yyscanner);
 
 						new = palloc(sizeof(PLpgSQL_stmt_dynexecute));
 						new->cmd_type = PLPGSQL_STMT_DYNEXECUTE;
@@ -2053,15 +2053,15 @@ stmt_dynexecute : K_EXECUTE
 							if (endtoken == K_INTO)
 							{
 								if (new->into)			/* multiple INTO */
-									yyerror(yyscanner, "syntax error");
+									yyerror(&yylloc, yyscanner, "syntax error");
 								new->into = true;
-								read_into_target(&new->target, &new->strict, yyscanner);
-								endtoken = yylex(yyscanner);
+								read_into_target(&new->target, &new->strict, &yylval, &yylloc, yyscanner);
+								endtoken = yylex(&yylval, &yylloc, yyscanner);
 							}
 							else if (endtoken == K_USING)
 							{
 								if (new->params)		/* multiple USING */
-									yyerror(yyscanner, "syntax error");
+									yyerror(&yylloc, yyscanner, "syntax error");
 								do
 								{
 									expr = read_sql_construct(',', ';', K_INTO,
@@ -2069,14 +2069,14 @@ stmt_dynexecute : K_EXECUTE
 															  RAW_PARSE_PLPGSQL_EXPR,
 															  true, true,
 															  NULL, &endtoken,
-															  yyscanner);
+															  &yylval, &yylloc, yyscanner);
 									new->params = lappend(new->params, expr);
 								} while (endtoken == ',');
 							}
 							else if (endtoken == ';')
 								break;
 							else
-								yyerror(yyscanner, "syntax error");
+								yyerror(&yylloc, yyscanner, "syntax error");
 						}
 
 						$$ = (PLpgSQL_stmt *) new;
@@ -2099,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(yyscanner);
+							tok = yylex(&yylval, &yylloc, yyscanner);
 							if (tok_is_keyword(tok, &yylval,
 											   K_NO, "no"))
 							{
-								tok = yylex(yyscanner);
+								tok = yylex(&yylval, &yylloc, yyscanner);
 								if (tok_is_keyword(tok, &yylval,
 												   K_SCROLL, "scroll"))
 								{
 									new->cursor_options |= CURSOR_OPT_NO_SCROLL;
-									tok = yylex(yyscanner);
+									tok = yylex(&yylval, &yylloc, yyscanner);
 								}
 							}
 							else if (tok_is_keyword(tok, &yylval,
 													K_SCROLL, "scroll"))
 							{
 								new->cursor_options |= CURSOR_OPT_SCROLL;
-								tok = yylex(yyscanner);
+								tok = yylex(&yylval, &yylloc, yyscanner);
 							}
 
 							if (tok != K_FOR)
-								yyerror(yyscanner, "syntax error, expected \"FOR\"");
+								yyerror(&yylloc, yyscanner, "syntax error, expected \"FOR\"");
 
-							tok = yylex(yyscanner);
+							tok = yylex(&yylval, &yylloc, yyscanner);
 							if (tok == K_EXECUTE)
 							{
 								int			endtoken;
@@ -2129,7 +2129,7 @@ stmt_open		: K_OPEN cursor_variable
 								new->dynquery =
 									read_sql_expression2(K_USING, ';',
 														 "USING or ;",
-														 &endtoken, yyscanner);
+														 &endtoken, &yylval, &yylloc, yyscanner);
 
 								/* If we found "USING", collect argument(s) */
 								if (endtoken == K_USING)
@@ -2140,7 +2140,7 @@ stmt_open		: K_OPEN cursor_variable
 									{
 										expr = read_sql_expression2(',', ';',
 																	", or ;",
-																	&endtoken, yyscanner);
+																	&endtoken, &yylval, &yylloc, yyscanner);
 										new->params = lappend(new->params,
 															  expr);
 									} while (endtoken == ',');
@@ -2148,14 +2148,14 @@ stmt_open		: K_OPEN cursor_variable
 							}
 							else
 							{
-								plpgsql_push_back_token(tok);
-								new->query = read_sql_stmt(yyscanner);
+								plpgsql_push_back_token(tok, &yylval, &yylloc);
+								new->query = read_sql_stmt(&yylval, &yylloc, yyscanner);
 							}
 						}
 						else
 						{
 							/* predefined cursor query, so read args */
-							new->argquery = read_cursor_args($2, ';', yyscanner);
+							new->argquery = read_cursor_args($2, ';', &yylval, &yylloc, yyscanner);
 						}
 
 						$$ = (PLpgSQL_stmt *) new;
@@ -2168,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, yyscanner);
+						read_into_target(&target, NULL, &yylval, &yylloc, yyscanner);
 
-						if (yylex(yyscanner) != ';')
-							yyerror(yyscanner, "syntax error");
+						if (yylex(&yylval, &yylloc, yyscanner) != ';')
+							yyerror(&yylloc, yyscanner, "syntax error");
 
 						/*
 						 * We don't allow multiple rows in PL/pgSQL's FETCH
@@ -2206,7 +2206,7 @@ stmt_move		: K_MOVE opt_fetch_direction cursor_variable ';'
 
 opt_fetch_direction	:
 					{
-						$$ = read_fetch_direction(yyscanner);
+						$$ = read_fetch_direction(&yylval, &yylloc, yyscanner);
 					}
 				;
 
@@ -2394,14 +2394,14 @@ proc_condition	: any_identifier
 								char   *sqlstatestr;
 
 								/* next token should be a string literal */
-								if (yylex(yyscanner) != SCONST)
-									yyerror(yyscanner, "syntax error");
+								if (yylex(&yylval, &yylloc, yyscanner) != SCONST)
+									yyerror(&yylloc, yyscanner, "syntax error");
 								sqlstatestr = yylval.str;
 
 								if (strlen(sqlstatestr) != 5)
-									yyerror(yyscanner, "invalid SQLSTATE code");
+									yyerror(&yylloc, yyscanner, "invalid SQLSTATE code");
 								if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5)
-									yyerror(yyscanner, "invalid SQLSTATE code");
+									yyerror(&yylloc, yyscanner, "invalid SQLSTATE code");
 
 								new = palloc(sizeof(PLpgSQL_condition));
 								new->sqlerrstate =
@@ -2419,15 +2419,15 @@ proc_condition	: any_identifier
 				;
 
 expr_until_semi :
-					{ $$ = read_sql_expression(';', ";", yyscanner); }
+					{ $$ = read_sql_expression(';', ";", &yylval, &yylloc, yyscanner); }
 				;
 
 expr_until_then :
-					{ $$ = read_sql_expression(K_THEN, "THEN", yyscanner); }
+					{ $$ = read_sql_expression(K_THEN, "THEN", &yylval, &yylloc, yyscanner); }
 				;
 
 expr_until_loop :
-					{ $$ = read_sql_expression(K_LOOP, "LOOP", yyscanner); }
+					{ $$ = read_sql_expression(K_LOOP, "LOOP", &yylval, &yylloc, yyscanner); }
 				;
 
 opt_block_label	:
@@ -2485,7 +2485,7 @@ any_identifier	: T_WORD
 				| T_DATUM
 					{
 						if ($1.ident == NULL) /* composite name not OK */
-							yyerror(yyscanner, "syntax error");
+							yyerror(&yylloc, yyscanner, "syntax error");
 						$$ = $1.ident;
 					}
 				;
@@ -2637,45 +2637,45 @@ cword_is_not_variable(PLcword *cword, int location)
  * look at yylval and yylloc.
  */
 static void
-current_token_is_not_variable(int tok, yyscan_t yyscanner)
+current_token_is_not_variable(int tok, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
 {
 	if (tok == T_WORD)
-		word_is_not_variable(&(yylval.word), yylloc);
+		word_is_not_variable(&(yylvalp->word), *yyllocp);
 	else if (tok == T_CWORD)
-		cword_is_not_variable(&(yylval.cword), yylloc);
+		cword_is_not_variable(&(yylvalp->cword), *yyllocp);
 	else
-		yyerror(yyscanner, "syntax error");
+		yyerror(yyllocp, yyscanner, "syntax error");
 }
 
 /* Convenience routine to read an expression with one possible terminator */
 static PLpgSQL_expr *
-read_sql_expression(int until, const char *expected, yyscan_t yyscanner)
+read_sql_expression(int until, const char *expected, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
 {
 	return read_sql_construct(until, 0, 0, expected,
 							  RAW_PARSE_PLPGSQL_EXPR,
 							  true, true, NULL, NULL,
-							  yyscanner);
+							  yylvalp, yyllocp, 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, yyscan_t yyscanner)
+					 int *endtoken, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
 {
 	return read_sql_construct(until, until2, 0, expected,
 							  RAW_PARSE_PLPGSQL_EXPR,
 							  true, true, NULL, endtoken,
-							  yyscanner);
+							  yylvalp, yyllocp, yyscanner);
 }
 
 /* Convenience routine to read a SQL statement that must end with ';' */
 static PLpgSQL_expr *
-read_sql_stmt(yyscan_t yyscanner)
+read_sql_stmt(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
 {
 	return read_sql_construct(';', 0, 0, ";",
 							  RAW_PARSE_DEFAULT,
 							  false, true, NULL, NULL,
-							  yyscanner);
+							  yylvalp, yyllocp, yyscanner);
 }
 
 /*
@@ -2702,6 +2702,7 @@ read_sql_construct(int until,
 				   bool valid_sql,
 				   int *startloc,
 				   int *endtoken,
+				   YYSTYPE *yylvalp, YYLTYPE *yyllocp,
 				   yyscan_t yyscanner)
 {
 	int			tok;
@@ -2720,9 +2721,9 @@ read_sql_construct(int until,
 
 	for (;;)
 	{
-		tok = yylex(yyscanner);
+		tok = yylex(yylvalp, yyllocp, yyscanner);
 		if (startlocation < 0)	/* remember loc of first token */
-			startlocation = yylloc;
+			startlocation = *yyllocp;
 		if (tok == until && parenlevel == 0)
 			break;
 		if (tok == until2 && parenlevel == 0)
@@ -2735,7 +2736,7 @@ read_sql_construct(int until,
 		{
 			parenlevel--;
 			if (parenlevel < 0)
-				yyerror(yyscanner, "mismatched parentheses");
+				yyerror(yyllocp, yyscanner, "mismatched parentheses");
 		}
 
 		/*
@@ -2746,22 +2747,22 @@ read_sql_construct(int until,
 		if (tok == 0 || tok == ';')
 		{
 			if (parenlevel != 0)
-				yyerror(yyscanner, "mismatched parentheses");
+				yyerror(yyllocp, yyscanner, "mismatched parentheses");
 			if (isexpression)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
 						 errmsg("missing \"%s\" at end of SQL expression",
 								expected),
-						 parser_errposition(yylloc)));
+						 parser_errposition(*yyllocp)));
 			else
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
 						 errmsg("missing \"%s\" at end of SQL statement",
 								expected),
-						 parser_errposition(yylloc)));
+						 parser_errposition(*yyllocp)));
 		}
 		/* Remember end+1 location of last accepted token */
-		endlocation = yylloc + plpgsql_token_length();
+		endlocation = *yyllocp + plpgsql_token_length();
 	}
 
 	plpgsql_IdentifierLookup = save_IdentifierLookup;
@@ -2775,9 +2776,9 @@ read_sql_construct(int until,
 	if (startlocation >= endlocation)
 	{
 		if (isexpression)
-			yyerror(yyscanner, "missing expression");
+			yyerror(yyllocp, yyscanner, "missing expression");
 		else
-			yyerror(yyscanner, "missing SQL statement");
+			yyerror(yyllocp, yyscanner, "missing SQL statement");
 	}
 
 	/*
@@ -2809,7 +2810,7 @@ read_sql_construct(int until,
  * Returns a PLpgSQL_type struct.
  */
 static PLpgSQL_type *
-read_datatype(int tok, yyscan_t yyscanner)
+read_datatype(int tok, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
 {
 	StringInfoData ds;
 	char	   *type_name;
@@ -2822,10 +2823,10 @@ read_datatype(int tok, yyscan_t yyscanner)
 
 	/* Often there will be a lookahead token, but if not, get one */
 	if (tok == YYEMPTY)
-		tok = yylex(yyscanner);
+		tok = yylex(yylvalp, yyllocp, yyscanner);
 
 	/* The current token is the start of what we'll pass to parse_datatype */
-	startlocation = yylloc;
+	startlocation = *yyllocp;
 
 	/*
 	 * If we have a simple or composite identifier, check for %TYPE and
@@ -2833,48 +2834,48 @@ read_datatype(int tok, yyscan_t yyscanner)
 	 */
 	if (tok == T_WORD)
 	{
-		char	   *dtname = yylval.word.ident;
+		char	   *dtname = yylvalp->word.ident;
 
-		tok = yylex(yyscanner);
+		tok = yylex(yylvalp, yyllocp, yyscanner);
 		if (tok == '%')
 		{
-			tok = yylex(yyscanner);
-			if (tok_is_keyword(tok, &yylval,
+			tok = yylex(yylvalp, yyllocp, yyscanner);
+			if (tok_is_keyword(tok, yylvalp,
 							   K_TYPE, "type"))
 				result = plpgsql_parse_wordtype(dtname);
-			else if (tok_is_keyword(tok, &yylval,
+			else if (tok_is_keyword(tok, yylvalp,
 									K_ROWTYPE, "rowtype"))
 				result = plpgsql_parse_wordrowtype(dtname);
 		}
 	}
 	else if (plpgsql_token_is_unreserved_keyword(tok))
 	{
-		char	   *dtname = pstrdup(yylval.keyword);
+		char	   *dtname = pstrdup(yylvalp->keyword);
 
-		tok = yylex(yyscanner);
+		tok = yylex(yylvalp, yyllocp, yyscanner);
 		if (tok == '%')
 		{
-			tok = yylex(yyscanner);
-			if (tok_is_keyword(tok, &yylval,
+			tok = yylex(yylvalp, yyllocp, yyscanner);
+			if (tok_is_keyword(tok, yylvalp,
 							   K_TYPE, "type"))
 				result = plpgsql_parse_wordtype(dtname);
-			else if (tok_is_keyword(tok, &yylval,
+			else if (tok_is_keyword(tok, yylvalp,
 									K_ROWTYPE, "rowtype"))
 				result = plpgsql_parse_wordrowtype(dtname);
 		}
 	}
 	else if (tok == T_CWORD)
 	{
-		List	   *dtnames = yylval.cword.idents;
+		List	   *dtnames = yylvalp->cword.idents;
 
-		tok = yylex(yyscanner);
+		tok = yylex(yylvalp, yyllocp, yyscanner);
 		if (tok == '%')
 		{
-			tok = yylex(yyscanner);
-			if (tok_is_keyword(tok, &yylval,
+			tok = yylex(yylvalp, yyllocp, yyscanner);
+			if (tok_is_keyword(tok, yylvalp,
 							   K_TYPE, "type"))
 				result = plpgsql_parse_cwordtype(dtnames);
-			else if (tok_is_keyword(tok, &yylval,
+			else if (tok_is_keyword(tok, yylvalp,
 									K_ROWTYPE, "rowtype"))
 				result = plpgsql_parse_cwordrowtype(dtnames);
 		}
@@ -2892,24 +2893,24 @@ read_datatype(int tok, yyscan_t yyscanner)
 	{
 		bool		is_array = false;
 
-		tok = yylex(yyscanner);
-		if (tok_is_keyword(tok, &yylval,
+		tok = yylex(yylvalp, yyllocp, yyscanner);
+		if (tok_is_keyword(tok, yylvalp,
 						   K_ARRAY, "array"))
 		{
 			is_array = true;
-			tok = yylex(yyscanner);
+			tok = yylex(yylvalp, yyllocp, yyscanner);
 		}
 		while (tok == '[')
 		{
 			is_array = true;
-			tok = yylex(yyscanner);
+			tok = yylex(yylvalp, yyllocp, yyscanner);
 			if (tok == ICONST)
-				tok = yylex(yyscanner);
+				tok = yylex(yylvalp, yyllocp, yyscanner);
 			if (tok != ']')
-				yyerror(yyscanner, "syntax error, expected \"]\"");
-			tok = yylex(yyscanner);
+				yyerror(yyllocp, yyscanner, "syntax error, expected \"]\"");
+			tok = yylex(yylvalp, yyllocp, yyscanner);
 		}
-		plpgsql_push_back_token(tok);
+		plpgsql_push_back_token(tok, yylvalp, yyllocp);
 
 		if (is_array)
 			result = plpgsql_build_datatype_arrayof(result);
@@ -2928,9 +2929,9 @@ read_datatype(int tok, yyscan_t yyscanner)
 		if (tok == 0)
 		{
 			if (parenlevel != 0)
-				yyerror(yyscanner, "mismatched parentheses");
+				yyerror(yyllocp, yyscanner, "mismatched parentheses");
 			else
-				yyerror(yyscanner, "incomplete data type declaration");
+				yyerror(yyllocp, yyscanner, "incomplete data type declaration");
 		}
 		/* Possible followers for datatype in a declaration */
 		if (tok == K_COLLATE || tok == K_NOT ||
@@ -2944,22 +2945,22 @@ read_datatype(int tok, yyscan_t yyscanner)
 		else if (tok == ')')
 			parenlevel--;
 
-		tok = yylex(yyscanner);
+		tok = yylex(yylvalp, yyllocp, yyscanner);
 	}
 
 	/* set up ds to contain complete typename text */
 	initStringInfo(&ds);
-	plpgsql_append_source_text(&ds, startlocation, yylloc);
+	plpgsql_append_source_text(&ds, startlocation, *yyllocp);
 	type_name = ds.data;
 
 	if (type_name[0] == '\0')
-		yyerror(yyscanner, "missing data type declaration");
+		yyerror(yyllocp, yyscanner, "missing data type declaration");
 
 	result = parse_datatype(type_name, startlocation);
 
 	pfree(ds.data);
 
-	plpgsql_push_back_token(tok);
+	plpgsql_push_back_token(tok, yylvalp, yyllocp);
 
 	return result;
 }
@@ -2970,7 +2971,7 @@ read_datatype(int tok, yyscan_t yyscanner)
  * 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, yyscan_t yyscanner)
+make_execsql_stmt(int firsttoken, int location, PLword *word, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
 {
 	StringInfoData ds;
 	IdentifierLookup save_IdentifierLookup;
@@ -3039,22 +3040,22 @@ make_execsql_stmt(int firsttoken, int location, PLword *word, yyscan_t yyscanner
 	for (;;)
 	{
 		prev_tok = tok;
-		tok = yylex(yyscanner);
+		tok = yylex(yylvalp, yyllocp, yyscanner);
 		if (have_into && into_end_loc < 0)
-			into_end_loc = yylloc;	/* token after the INTO part */
+			into_end_loc = *yyllocp;	/* token after the INTO part */
 		/* Detect CREATE [OR REPLACE] {FUNCTION|PROCEDURE} */
 		if (tokens[0] == 'c' && token_count < sizeof(tokens))
 		{
 			if (tok == K_OR)
 				tokens[token_count] = 'o';
 			else if (tok == T_WORD &&
-					 strcmp(yylval.word.ident, "replace") == 0)
+					 strcmp(yylvalp->word.ident, "replace") == 0)
 				tokens[token_count] = 'r';
 			else if (tok == T_WORD &&
-					 strcmp(yylval.word.ident, "function") == 0)
+					 strcmp(yylvalp->word.ident, "function") == 0)
 				tokens[token_count] = 'f';
 			else if (tok == T_WORD &&
-					 strcmp(yylval.word.ident, "procedure") == 0)
+					 strcmp(yylvalp->word.ident, "procedure") == 0)
 				tokens[token_count] = 'f';	/* treat same as "function" */
 			if (tokens[1] == 'f' ||
 				(tokens[1] == 'o' && tokens[2] == 'r' && tokens[3] == 'f'))
@@ -3078,7 +3079,7 @@ make_execsql_stmt(int firsttoken, int location, PLword *word, yyscan_t yyscanner
 		if (tok == ';' && paren_depth == 0 && begin_depth == 0)
 			break;
 		if (tok == 0)
-			yyerror(yyscanner, "unexpected end of function definition");
+			yyerror(yyllocp, yyscanner, "unexpected end of function definition");
 		if (tok == K_INTO)
 		{
 			if (prev_tok == K_INSERT)
@@ -3088,11 +3089,11 @@ make_execsql_stmt(int firsttoken, int location, PLword *word, yyscan_t yyscanner
 			if (firsttoken == K_IMPORT)
 				continue;		/* IMPORT ... INTO is not an INTO-target */
 			if (have_into)
-				yyerror(yyscanner, "INTO specified more than once");
+				yyerror(yyllocp, yyscanner, "INTO specified more than once");
 			have_into = true;
-			into_start_loc = yylloc;
+			into_start_loc = *yyllocp;
 			plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL;
-			read_into_target(&target, &have_strict, yyscanner);
+			read_into_target(&target, &have_strict, yylvalp, yyllocp, yyscanner);
 			plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_EXPR;
 		}
 	}
@@ -3108,10 +3109,10 @@ make_execsql_stmt(int firsttoken, int location, PLword *word, yyscan_t yyscanner
 		 */
 		plpgsql_append_source_text(&ds, location, into_start_loc);
 		appendStringInfoSpaces(&ds, into_end_loc - into_start_loc);
-		plpgsql_append_source_text(&ds, into_end_loc, yylloc);
+		plpgsql_append_source_text(&ds, into_end_loc, *yyllocp);
 	}
 	else
-		plpgsql_append_source_text(&ds, location, yylloc);
+		plpgsql_append_source_text(&ds, location, *yyllocp);
 
 	/* trim any trailing whitespace, for neatness */
 	while (ds.len > 0 && scanner_isspace(ds.data[ds.len - 1]))
@@ -3145,7 +3146,7 @@ make_execsql_stmt(int firsttoken, int location, PLword *word, yyscan_t yyscanner
  * Read FETCH or MOVE direction clause (everything through FROM/IN).
  */
 static PLpgSQL_stmt_fetch *
-read_fetch_direction(yyscan_t yyscanner)
+read_fetch_direction(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
 {
 	PLpgSQL_stmt_fetch *fetch;
 	int			tok;
@@ -3164,65 +3165,65 @@ read_fetch_direction(yyscan_t yyscanner)
 	fetch->expr = NULL;
 	fetch->returns_multiple_rows = false;
 
-	tok = yylex(yyscanner);
+	tok = yylex(yylvalp, yyllocp, yyscanner);
 	if (tok == 0)
-		yyerror(yyscanner, "unexpected end of function definition");
+		yyerror(yyllocp, yyscanner, "unexpected end of function definition");
 
-	if (tok_is_keyword(tok, &yylval,
+	if (tok_is_keyword(tok, yylvalp,
 					   K_NEXT, "next"))
 	{
 		/* use defaults */
 	}
-	else if (tok_is_keyword(tok, &yylval,
+	else if (tok_is_keyword(tok, yylvalp,
 							K_PRIOR, "prior"))
 	{
 		fetch->direction = FETCH_BACKWARD;
 	}
-	else if (tok_is_keyword(tok, &yylval,
+	else if (tok_is_keyword(tok, yylvalp,
 							K_FIRST, "first"))
 	{
 		fetch->direction = FETCH_ABSOLUTE;
 	}
-	else if (tok_is_keyword(tok, &yylval,
+	else if (tok_is_keyword(tok, yylvalp,
 							K_LAST, "last"))
 	{
 		fetch->direction = FETCH_ABSOLUTE;
 		fetch->how_many = -1;
 	}
-	else if (tok_is_keyword(tok, &yylval,
+	else if (tok_is_keyword(tok, yylvalp,
 							K_ABSOLUTE, "absolute"))
 	{
 		fetch->direction = FETCH_ABSOLUTE;
 		fetch->expr = read_sql_expression2(K_FROM, K_IN,
 										   "FROM or IN",
-										   NULL, yyscanner);
+										   NULL, yylvalp, yyllocp, yyscanner);
 		check_FROM = false;
 	}
-	else if (tok_is_keyword(tok, &yylval,
+	else if (tok_is_keyword(tok, yylvalp,
 							K_RELATIVE, "relative"))
 	{
 		fetch->direction = FETCH_RELATIVE;
 		fetch->expr = read_sql_expression2(K_FROM, K_IN,
 										   "FROM or IN",
-										   NULL, yyscanner);
+										   NULL, yylvalp, yyllocp, yyscanner);
 		check_FROM = false;
 	}
-	else if (tok_is_keyword(tok, &yylval,
+	else if (tok_is_keyword(tok, yylvalp,
 							K_ALL, "all"))
 	{
 		fetch->how_many = FETCH_ALL;
 		fetch->returns_multiple_rows = true;
 	}
-	else if (tok_is_keyword(tok, &yylval,
+	else if (tok_is_keyword(tok, yylvalp,
 							K_FORWARD, "forward"))
 	{
-		complete_direction(fetch, &check_FROM, yyscanner);
+		complete_direction(fetch, &check_FROM, yylvalp, yyllocp, yyscanner);
 	}
-	else if (tok_is_keyword(tok, &yylval,
+	else if (tok_is_keyword(tok, yylvalp,
 							K_BACKWARD, "backward"))
 	{
 		fetch->direction = FETCH_BACKWARD;
-		complete_direction(fetch, &check_FROM, yyscanner);
+		complete_direction(fetch, &check_FROM, yylvalp, yyllocp, yyscanner);
 	}
 	else if (tok == K_FROM || tok == K_IN)
 	{
@@ -3232,7 +3233,7 @@ read_fetch_direction(yyscan_t yyscanner)
 	else if (tok == T_DATUM)
 	{
 		/* Assume there's no direction clause and tok is a cursor name */
-		plpgsql_push_back_token(tok);
+		plpgsql_push_back_token(tok, yylvalp, yyllocp);
 		check_FROM = false;
 	}
 	else
@@ -3245,10 +3246,10 @@ read_fetch_direction(yyscan_t yyscanner)
 		 * will trigger.  Perhaps this can be improved someday, but it hardly
 		 * seems worth a lot of work.
 		 */
-		plpgsql_push_back_token(tok);
+		plpgsql_push_back_token(tok, yylvalp, yyllocp);
 		fetch->expr = read_sql_expression2(K_FROM, K_IN,
 										   "FROM or IN",
-										   NULL, yyscanner);
+										   NULL, yylvalp, yyllocp, yyscanner);
 		fetch->returns_multiple_rows = true;
 		check_FROM = false;
 	}
@@ -3256,9 +3257,9 @@ read_fetch_direction(yyscan_t yyscanner)
 	/* check FROM or IN keyword after direction's specification */
 	if (check_FROM)
 	{
-		tok = yylex(yyscanner);
+		tok = yylex(yylvalp, yyllocp, yyscanner);
 		if (tok != K_FROM && tok != K_IN)
-			yyerror(yyscanner, "expected FROM or IN");
+			yyerror(yyllocp, yyscanner, "expected FROM or IN");
 	}
 
 	return fetch;
@@ -3271,13 +3272,13 @@ read_fetch_direction(yyscan_t yyscanner)
  *   BACKWARD expr, BACKWARD ALL, BACKWARD
  */
 static void
-complete_direction(PLpgSQL_stmt_fetch *fetch, bool *check_FROM, yyscan_t yyscanner)
+complete_direction(PLpgSQL_stmt_fetch *fetch, bool *check_FROM, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
 {
 	int			tok;
 
-	tok = yylex(yyscanner);
+	tok = yylex(yylvalp, yyllocp, yyscanner);
 	if (tok == 0)
-		yyerror(yyscanner, "unexpected end of function definition");
+		yyerror(yyllocp, yyscanner, "unexpected end of function definition");
 
 	if (tok == K_FROM || tok == K_IN)
 	{
@@ -3293,17 +3294,17 @@ complete_direction(PLpgSQL_stmt_fetch *fetch, bool *check_FROM, yyscan_t yyscann
 		return;
 	}
 
-	plpgsql_push_back_token(tok);
+	plpgsql_push_back_token(tok, yylvalp, yyllocp);
 	fetch->expr = read_sql_expression2(K_FROM, K_IN,
 									   "FROM or IN",
-									   NULL, yyscanner);
+									   NULL, yylvalp, yyllocp, yyscanner);
 	fetch->returns_multiple_rows = true;
 	*check_FROM = false;
 }
 
 
 static PLpgSQL_stmt *
-make_return_stmt(int location, yyscan_t yyscanner)
+make_return_stmt(int location, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
 {
 	PLpgSQL_stmt_return *new;
 
@@ -3316,36 +3317,36 @@ make_return_stmt(int location, yyscan_t yyscanner)
 
 	if (plpgsql_curr_compile->fn_retset)
 	{
-		if (yylex(yyscanner) != ';')
+		if (yylex(yylvalp, yyllocp, yyscanner) != ';')
 			ereport(ERROR,
 					(errcode(ERRCODE_DATATYPE_MISMATCH),
 					 errmsg("RETURN cannot have a parameter in function returning set"),
 					 errhint("Use RETURN NEXT or RETURN QUERY."),
-					 parser_errposition(yylloc)));
+					 parser_errposition(*yyllocp)));
 	}
 	else if (plpgsql_curr_compile->fn_rettype == VOIDOID)
 	{
-		if (yylex(yyscanner) != ';')
+		if (yylex(yylvalp, yyllocp, yyscanner) != ';')
 		{
 			if (plpgsql_curr_compile->fn_prokind == PROKIND_PROCEDURE)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
 						 errmsg("RETURN cannot have a parameter in a procedure"),
-						 parser_errposition(yylloc)));
+						 parser_errposition(*yyllocp)));
 			else
 				ereport(ERROR,
 						(errcode(ERRCODE_DATATYPE_MISMATCH),
 						 errmsg("RETURN cannot have a parameter in function returning void"),
-						 parser_errposition(yylloc)));
+						 parser_errposition(*yyllocp)));
 		}
 	}
 	else if (plpgsql_curr_compile->out_param_varno >= 0)
 	{
-		if (yylex(yyscanner) != ';')
+		if (yylex(yylvalp, yyllocp, yyscanner) != ';')
 			ereport(ERROR,
 					(errcode(ERRCODE_DATATYPE_MISMATCH),
 					 errmsg("RETURN cannot have a parameter in function with OUT parameters"),
-					 parser_errposition(yylloc)));
+					 parser_errposition(*yyllocp)));
 		new->retvarno = plpgsql_curr_compile->out_param_varno;
 	}
 	else
@@ -3354,17 +3355,17 @@ make_return_stmt(int location, yyscan_t yyscanner)
 		 * We want to special-case simple variable references for efficiency.
 		 * So peek ahead to see if that's what we have.
 		 */
-		int			tok = yylex(yyscanner);
+		int			tok = yylex(yylvalp, yyllocp, yyscanner);
 
 		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 ||
-			 yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_REC))
+			(yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_VAR ||
+			 yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_PROMISE ||
+			 yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
+			 yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_REC))
 		{
-			new->retvarno = yylval.wdatum.datum->dno;
+			new->retvarno = yylvalp->wdatum.datum->dno;
 			/* eat the semicolon token that we only peeked at above */
-			tok = yylex(yyscanner);
+			tok = yylex(yylvalp, yyllocp, yyscanner);
 			Assert(tok == ';');
 		}
 		else
@@ -3375,8 +3376,8 @@ make_return_stmt(int location, yyscan_t yyscanner)
 			 * Note that a well-formed expression is _required_ here; anything
 			 * else is a compile-time error.
 			 */
-			plpgsql_push_back_token(tok);
-			new->expr = read_sql_expression(';', ";", yyscanner);
+			plpgsql_push_back_token(tok, yylvalp, yyllocp);
+			new->expr = read_sql_expression(';', ";", yylvalp, yyllocp, yyscanner);
 		}
 	}
 
@@ -3385,7 +3386,7 @@ make_return_stmt(int location, yyscan_t yyscanner)
 
 
 static PLpgSQL_stmt *
-make_return_next_stmt(int location, yyscan_t yyscanner)
+make_return_next_stmt(int location, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
 {
 	PLpgSQL_stmt_return_next *new;
 
@@ -3404,11 +3405,11 @@ make_return_next_stmt(int location, yyscan_t yyscanner)
 
 	if (plpgsql_curr_compile->out_param_varno >= 0)
 	{
-		if (yylex(yyscanner) != ';')
+		if (yylex(yylvalp, yyllocp, yyscanner) != ';')
 			ereport(ERROR,
 					(errcode(ERRCODE_DATATYPE_MISMATCH),
 					 errmsg("RETURN NEXT cannot have a parameter in function with OUT parameters"),
-					 parser_errposition(yylloc)));
+					 parser_errposition(*yyllocp)));
 		new->retvarno = plpgsql_curr_compile->out_param_varno;
 	}
 	else
@@ -3417,17 +3418,17 @@ make_return_next_stmt(int location, yyscan_t yyscanner)
 		 * We want to special-case simple variable references for efficiency.
 		 * So peek ahead to see if that's what we have.
 		 */
-		int			tok = yylex(yyscanner);
+		int			tok = yylex(yylvalp, yyllocp, yyscanner);
 
 		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 ||
-			 yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_REC))
+			(yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_VAR ||
+			 yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_PROMISE ||
+			 yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
+			 yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_REC))
 		{
-			new->retvarno = yylval.wdatum.datum->dno;
+			new->retvarno = yylvalp->wdatum.datum->dno;
 			/* eat the semicolon token that we only peeked at above */
-			tok = yylex(yyscanner);
+			tok = yylex(yylvalp, yyllocp, yyscanner);
 			Assert(tok == ';');
 		}
 		else
@@ -3438,8 +3439,8 @@ make_return_next_stmt(int location, yyscan_t yyscanner)
 			 * Note that a well-formed expression is _required_ here; anything
 			 * else is a compile-time error.
 			 */
-			plpgsql_push_back_token(tok);
-			new->expr = read_sql_expression(';', ";", yyscanner);
+			plpgsql_push_back_token(tok, yylvalp, yyllocp);
+			new->expr = read_sql_expression(';', ";", yylvalp, yyllocp, yyscanner);
 		}
 	}
 
@@ -3448,7 +3449,7 @@ make_return_next_stmt(int location, yyscan_t yyscanner)
 
 
 static PLpgSQL_stmt *
-make_return_query_stmt(int location, yyscan_t yyscanner)
+make_return_query_stmt(int location, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
 {
 	PLpgSQL_stmt_return_query *new;
 	int			tok;
@@ -3465,11 +3466,11 @@ make_return_query_stmt(int location, yyscan_t yyscanner)
 	new->stmtid = ++plpgsql_curr_compile->nstatements;
 
 	/* check for RETURN QUERY EXECUTE */
-	if ((tok = yylex(yyscanner)) != K_EXECUTE)
+	if ((tok = yylex(yylvalp, yyllocp, yyscanner)) != K_EXECUTE)
 	{
 		/* ordinary static query */
-		plpgsql_push_back_token(tok);
-		new->query = read_sql_stmt(yyscanner);
+		plpgsql_push_back_token(tok, yylvalp, yyllocp);
+		new->query = read_sql_stmt(yylvalp, yyllocp, yyscanner);
 	}
 	else
 	{
@@ -3477,14 +3478,14 @@ make_return_query_stmt(int location, yyscan_t yyscanner)
 		int			term;
 
 		new->dynquery = read_sql_expression2(';', K_USING, "; or USING",
-											 &term, yyscanner);
+											 &term, yylvalp, yyllocp, yyscanner);
 		if (term == K_USING)
 		{
 			do
 			{
 				PLpgSQL_expr *expr;
 
-				expr = read_sql_expression2(',', ';', ", or ;", &term, yyscanner);
+				expr = read_sql_expression2(',', ';', ", or ;", &term, yylvalp, yyllocp, yyscanner);
 				new->params = lappend(new->params, expr);
 			} while (term == ',');
 		}
@@ -3538,7 +3539,7 @@ check_assignable(PLpgSQL_datum *datum, int location)
  * INTO keyword.
  */
 static void
-read_into_target(PLpgSQL_variable **target, bool *strict, yyscan_t yyscanner)
+read_into_target(PLpgSQL_variable **target, bool *strict, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
 {
 	int			tok;
 
@@ -3547,11 +3548,11 @@ read_into_target(PLpgSQL_variable **target, bool *strict, yyscan_t yyscanner)
 	if (strict)
 		*strict = false;
 
-	tok = yylex(yyscanner);
+	tok = yylex(yylvalp, yyllocp, yyscanner);
 	if (strict && tok == K_STRICT)
 	{
 		*strict = true;
-		tok = yylex(yyscanner);
+		tok = yylex(yylvalp, yyllocp, yyscanner);
 	}
 
 	/*
@@ -3564,30 +3565,30 @@ read_into_target(PLpgSQL_variable **target, bool *strict, yyscan_t yyscanner)
 	switch (tok)
 	{
 		case T_DATUM:
-			if (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
-				yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_REC)
+			if (yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
+				yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_REC)
 			{
-				check_assignable(yylval.wdatum.datum, yylloc);
-				*target = (PLpgSQL_variable *) yylval.wdatum.datum;
+				check_assignable(yylvalp->wdatum.datum, *yyllocp);
+				*target = (PLpgSQL_variable *) yylvalp->wdatum.datum;
 
-				if ((tok = yylex(yyscanner)) == ',')
+				if ((tok = yylex(yylvalp, yyllocp, yyscanner)) == ',')
 					ereport(ERROR,
 							(errcode(ERRCODE_SYNTAX_ERROR),
 							 errmsg("record variable cannot be part of multiple-item INTO list"),
-							 parser_errposition(yylloc)));
-				plpgsql_push_back_token(tok);
+							 parser_errposition(*yyllocp)));
+				plpgsql_push_back_token(tok, yylvalp, yyllocp);
 			}
 			else
 			{
 				*target = (PLpgSQL_variable *)
-					read_into_scalar_list(NameOfDatum(&(yylval.wdatum)),
-										  yylval.wdatum.datum, yylloc, yyscanner);
+					read_into_scalar_list(NameOfDatum(&(yylvalp->wdatum)),
+										  yylvalp->wdatum.datum, *yyllocp, yylvalp, yyllocp, yyscanner);
 			}
 			break;
 
 		default:
 			/* just to give a better message than "syntax error" */
-			current_token_is_not_variable(tok, yyscanner);
+			current_token_is_not_variable(tok, yylvalp, yyllocp, yyscanner);
 	}
 }
 
@@ -3601,6 +3602,7 @@ static PLpgSQL_row *
 read_into_scalar_list(char *initial_name,
 					  PLpgSQL_datum *initial_datum,
 					  int initial_location,
+					  YYSTYPE *yylvalp, YYLTYPE *yyllocp,
 					  yyscan_t yyscanner)
 {
 	int			nfields;
@@ -3614,34 +3616,34 @@ read_into_scalar_list(char *initial_name,
 	varnos[0] = initial_datum->dno;
 	nfields = 1;
 
-	while ((tok = yylex(yyscanner)) == ',')
+	while ((tok = yylex(yylvalp, yyllocp, yyscanner)) == ',')
 	{
 		/* Check for array overflow */
 		if (nfields >= 1024)
 			ereport(ERROR,
 					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 					 errmsg("too many INTO variables specified"),
-					 parser_errposition(yylloc)));
+					 parser_errposition(*yyllocp)));
 
-		tok = yylex(yyscanner);
+		tok = yylex(yylvalp, yyllocp, yyscanner);
 		switch (tok)
 		{
 			case T_DATUM:
-				check_assignable(yylval.wdatum.datum, yylloc);
-				if (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
-					yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_REC)
+				check_assignable(yylvalp->wdatum.datum, *yyllocp);
+				if (yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
+					yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_REC)
 					ereport(ERROR,
 							(errcode(ERRCODE_SYNTAX_ERROR),
 							 errmsg("\"%s\" is not a scalar variable",
-									NameOfDatum(&(yylval.wdatum))),
-							 parser_errposition(yylloc)));
-				fieldnames[nfields] = NameOfDatum(&(yylval.wdatum));
-				varnos[nfields++] = yylval.wdatum.datum->dno;
+									NameOfDatum(&(yylvalp->wdatum))),
+							 parser_errposition(*yyllocp)));
+				fieldnames[nfields] = NameOfDatum(&(yylvalp->wdatum));
+				varnos[nfields++] = yylvalp->wdatum.datum->dno;
 				break;
 
 			default:
 				/* just to give a better message than "syntax error" */
-				current_token_is_not_variable(tok, yyscanner);
+				current_token_is_not_variable(tok, yylvalp, yyllocp, yyscanner);
 		}
 	}
 
@@ -3649,7 +3651,7 @@ read_into_scalar_list(char *initial_name,
 	 * We read an extra, non-comma token from yylex(), so push it back onto
 	 * the input stream
 	 */
-	plpgsql_push_back_token(tok);
+	plpgsql_push_back_token(tok, yylvalp, yyllocp);
 
 	row = palloc0(sizeof(PLpgSQL_row));
 	row->dtype = PLPGSQL_DTYPE_ROW;
@@ -3852,7 +3854,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, yyscan_t yyscanner)
+read_cursor_args(PLpgSQL_var *cursor, int until, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
 {
 	PLpgSQL_expr *expr;
 	PLpgSQL_row *row;
@@ -3862,7 +3864,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until, yyscan_t yyscanner)
 	StringInfoData ds;
 	bool		any_named = false;
 
-	tok = yylex(yyscanner);
+	tok = yylex(yylvalp, yyllocp, yyscanner);
 	if (cursor->cursor_explicit_argrow < 0)
 	{
 		/* No arguments expected */
@@ -3871,10 +3873,10 @@ read_cursor_args(PLpgSQL_var *cursor, int until, yyscan_t yyscanner)
 					(errcode(ERRCODE_SYNTAX_ERROR),
 					 errmsg("cursor \"%s\" has no arguments",
 							cursor->refname),
-					 parser_errposition(yylloc)));
+					 parser_errposition(*yyllocp)));
 
 		if (tok != until)
-			yyerror(yyscanner, "syntax error");
+			yyerror(yyllocp, yyscanner, "syntax error");
 
 		return NULL;
 	}
@@ -3885,7 +3887,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until, yyscan_t yyscanner)
 				(errcode(ERRCODE_SYNTAX_ERROR),
 				 errmsg("cursor \"%s\" has arguments",
 						cursor->refname),
-				 parser_errposition(yylloc)));
+				 parser_errposition(*yyllocp)));
 
 	/*
 	 * Read the arguments, one by one.
@@ -3912,8 +3914,8 @@ read_cursor_args(PLpgSQL_var *cursor, int until, yyscan_t yyscanner)
 			/* Read the argument name, ignoring any matching variable */
 			save_IdentifierLookup = plpgsql_IdentifierLookup;
 			plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_DECLARE;
-			yylex(yyscanner);
-			argname = yylval.str;
+			yylex(yylvalp, yyllocp, yyscanner);
+			argname = yylvalp->str;
 			plpgsql_IdentifierLookup = save_IdentifierLookup;
 
 			/* Match argument name to cursor arguments */
@@ -3927,15 +3929,15 @@ read_cursor_args(PLpgSQL_var *cursor, int until, yyscan_t yyscanner)
 						(errcode(ERRCODE_SYNTAX_ERROR),
 						 errmsg("cursor \"%s\" has no argument named \"%s\"",
 								cursor->refname, argname),
-						 parser_errposition(yylloc)));
+						 parser_errposition(*yyllocp)));
 
 			/*
 			 * Eat the ":=". We already peeked, so the error should never
 			 * happen.
 			 */
-			tok2 = yylex(yyscanner);
+			tok2 = yylex(yylvalp, yyllocp, yyscanner);
 			if (tok2 != COLON_EQUALS)
-				yyerror(yyscanner, "syntax error");
+				yyerror(yyllocp, yyscanner, "syntax error");
 
 			any_named = true;
 		}
@@ -3960,7 +3962,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until, yyscan_t yyscanner)
 								  RAW_PARSE_PLPGSQL_EXPR,
 								  true, true,
 								  NULL, &endtoken,
-								  yyscanner);
+								  yylvalp, yyllocp, yyscanner);
 
 		argv[argpos] = item->query;
 
@@ -3969,14 +3971,14 @@ read_cursor_args(PLpgSQL_var *cursor, int until, yyscan_t yyscanner)
 					(errcode(ERRCODE_SYNTAX_ERROR),
 					 errmsg("not enough arguments for cursor \"%s\"",
 							cursor->refname),
-					 parser_errposition(yylloc)));
+					 parser_errposition(*yyllocp)));
 
 		if (endtoken == ',' && (argc == row->nfields - 1))
 			ereport(ERROR,
 					(errcode(ERRCODE_SYNTAX_ERROR),
 					 errmsg("too many arguments for cursor \"%s\"",
 							cursor->refname),
-					 parser_errposition(yylloc)));
+					 parser_errposition(*yyllocp)));
 	}
 
 	/* Make positional argument list */
@@ -4007,9 +4009,9 @@ read_cursor_args(PLpgSQL_var *cursor, int until, yyscan_t yyscanner)
 	pfree(ds.data);
 
 	/* Next we'd better find the until token */
-	tok = yylex(yyscanner);
+	tok = yylex(yylvalp, yyllocp, yyscanner);
 	if (tok != until)
-		yyerror(yyscanner, "syntax error");
+		yyerror(yyllocp, yyscanner, "syntax error");
 
 	return expr;
 }
@@ -4018,7 +4020,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until, yyscan_t yyscanner)
  * Parse RAISE ... USING options
  */
 static List *
-read_raise_options(yyscan_t yyscanner)
+read_raise_options(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
 {
 	List	   *result = NIL;
 
@@ -4027,46 +4029,46 @@ read_raise_options(yyscan_t yyscanner)
 		PLpgSQL_raise_option *opt;
 		int			tok;
 
-		if ((tok = yylex(yyscanner)) == 0)
-			yyerror(yyscanner, "unexpected end of function definition");
+		if ((tok = yylex(yylvalp, yyllocp, yyscanner)) == 0)
+			yyerror(yyllocp, yyscanner, "unexpected end of function definition");
 
 		opt = (PLpgSQL_raise_option *) palloc(sizeof(PLpgSQL_raise_option));
 
-		if (tok_is_keyword(tok, &yylval,
+		if (tok_is_keyword(tok, yylvalp,
 						   K_ERRCODE, "errcode"))
 			opt->opt_type = PLPGSQL_RAISEOPTION_ERRCODE;
-		else if (tok_is_keyword(tok, &yylval,
+		else if (tok_is_keyword(tok, yylvalp,
 								K_MESSAGE, "message"))
 			opt->opt_type = PLPGSQL_RAISEOPTION_MESSAGE;
-		else if (tok_is_keyword(tok, &yylval,
+		else if (tok_is_keyword(tok, yylvalp,
 								K_DETAIL, "detail"))
 			opt->opt_type = PLPGSQL_RAISEOPTION_DETAIL;
-		else if (tok_is_keyword(tok, &yylval,
+		else if (tok_is_keyword(tok, yylvalp,
 								K_HINT, "hint"))
 			opt->opt_type = PLPGSQL_RAISEOPTION_HINT;
-		else if (tok_is_keyword(tok, &yylval,
+		else if (tok_is_keyword(tok, yylvalp,
 								K_COLUMN, "column"))
 			opt->opt_type = PLPGSQL_RAISEOPTION_COLUMN;
-		else if (tok_is_keyword(tok, &yylval,
+		else if (tok_is_keyword(tok, yylvalp,
 								K_CONSTRAINT, "constraint"))
 			opt->opt_type = PLPGSQL_RAISEOPTION_CONSTRAINT;
-		else if (tok_is_keyword(tok, &yylval,
+		else if (tok_is_keyword(tok, yylvalp,
 								K_DATATYPE, "datatype"))
 			opt->opt_type = PLPGSQL_RAISEOPTION_DATATYPE;
-		else if (tok_is_keyword(tok, &yylval,
+		else if (tok_is_keyword(tok, yylvalp,
 								K_TABLE, "table"))
 			opt->opt_type = PLPGSQL_RAISEOPTION_TABLE;
-		else if (tok_is_keyword(tok, &yylval,
+		else if (tok_is_keyword(tok, yylvalp,
 								K_SCHEMA, "schema"))
 			opt->opt_type = PLPGSQL_RAISEOPTION_SCHEMA;
 		else
-			yyerror(yyscanner, "unrecognized RAISE statement option");
+			yyerror(yyllocp, yyscanner, "unrecognized RAISE statement option");
 
-		tok = yylex(yyscanner);
+		tok = yylex(yylvalp, yyllocp, yyscanner);
 		if (tok != '=' && tok != COLON_EQUALS)
-			yyerror(yyscanner, "syntax error, expected \"=\"");
+			yyerror(yyllocp, yyscanner, "syntax error, expected \"=\"");
 
-		opt->expr = read_sql_expression2(',', ';', ", or ;", &tok, yyscanner);
+		opt->expr = read_sql_expression2(',', ';', ", or ;", &tok, yylvalp, yyllocp, yyscanner);
 
 		result = lappend(result, opt);
 
diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c
index eeb40c301db..abf2d40c07a 100644
--- a/src/pl/plpgsql/src/pl_scanner.c
+++ b/src/pl/plpgsql/src/pl_scanner.c
@@ -142,7 +142,7 @@ static void location_lineno_init(void);
  * matches one of those.
  */
 int
-plpgsql_yylex(yyscan_t yyscanner)
+plpgsql_yylex(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
 {
 	int			tok1;
 	TokenAuxData aux1;
@@ -296,8 +296,8 @@ plpgsql_yylex(yyscan_t yyscanner)
 		 */
 	}
 
-	plpgsql_yylval = aux1.lval;
-	plpgsql_yylloc = aux1.lloc;
+	*yylvalp = aux1.lval;
+	*yyllocp = aux1.lloc;
 	plpgsql_yyleng = aux1.leng;
 	plpgsql_yytoken = tok1;
 	return tok1;
@@ -383,12 +383,12 @@ push_back_token(int token, TokenAuxData *auxdata)
  * is not a good idea to push back a token code other than what you read.
  */
 void
-plpgsql_push_back_token(int token)
+plpgsql_push_back_token(int token, YYSTYPE *yylvalp, YYLTYPE *yyllocp)
 {
 	TokenAuxData auxdata;
 
-	auxdata.lval = plpgsql_yylval;
-	auxdata.lloc = plpgsql_yylloc;
+	auxdata.lval = *yylvalp;
+	auxdata.lloc = *yyllocp;
 	auxdata.leng = plpgsql_yyleng;
 	push_back_token(token, &auxdata);
 }
@@ -512,9 +512,9 @@ plpgsql_scanner_errposition(int location)
  * be misleading!
  */
 void
-plpgsql_yyerror(yyscan_t yyscanner, const char *message)
+plpgsql_yyerror(YYLTYPE *yyllocp, yyscan_t yyscanner, const char *message)
 {
-	char	   *yytext = core_yy_extra.scanbuf + plpgsql_yylloc;
+	char	   *yytext = core_yy_extra.scanbuf + *yyllocp;
 
 	if (*yytext == '\0')
 	{
@@ -522,7 +522,7 @@ plpgsql_yyerror(yyscan_t yyscanner, const char *message)
 				(errcode(ERRCODE_SYNTAX_ERROR),
 		/* translator: %s is typically the translation of "syntax error" */
 				 errmsg("%s at end of input", _(message)),
-				 plpgsql_scanner_errposition(plpgsql_yylloc)));
+				 plpgsql_scanner_errposition(*yyllocp)));
 	}
 	else
 	{
@@ -538,7 +538,7 @@ plpgsql_yyerror(yyscan_t yyscanner, const char *message)
 				(errcode(ERRCODE_SYNTAX_ERROR),
 		/* translator: first %s is typically the translation of "syntax error" */
 				 errmsg("%s at or near \"%s\"", _(message), yytext),
-				 plpgsql_scanner_errposition(plpgsql_yylloc)));
+				 plpgsql_scanner_errposition(*yyllocp)));
 	}
 }
 
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 07dcfa84f88..151ae7943df 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -1315,13 +1315,15 @@ extern void plpgsql_dumptree(PLpgSQL_function *func);
 /*
  * Scanner functions in pl_scanner.c
  */
+union YYSTYPE;
+#define YYLTYPE int
 #ifndef YY_TYPEDEF_YY_SCANNER_T
 #define YY_TYPEDEF_YY_SCANNER_T
 typedef void *yyscan_t;
 #endif
-extern int	plpgsql_yylex(yyscan_t yyscanner);
+extern int	plpgsql_yylex(union YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
 extern int	plpgsql_token_length(void);
-extern void plpgsql_push_back_token(int token);
+extern void plpgsql_push_back_token(int token, union YYSTYPE *yylvalp, YYLTYPE *yyllocp);
 extern bool plpgsql_token_is_unreserved_keyword(int token);
 extern void plpgsql_append_source_text(StringInfo buf,
 									   int startlocation, int endlocation);
@@ -1329,7 +1331,7 @@ extern int	plpgsql_peek(yyscan_t yyscanner);
 extern void plpgsql_peek2(int *tok1_p, int *tok2_p, int *tok1_loc,
 						  int *tok2_loc, yyscan_t yyscanner);
 extern int	plpgsql_scanner_errposition(int location);
-extern void plpgsql_yyerror(yyscan_t yyscanner, const char *message) pg_attribute_noreturn();
+extern void plpgsql_yyerror(YYLTYPE *yyllocp, yyscan_t yyscanner, const char *message) pg_attribute_noreturn();
 extern int	plpgsql_location_to_lineno(int location);
 extern int	plpgsql_latest_lineno(void);
 extern yyscan_t plpgsql_scanner_init(const char *str);
-- 
2.47.1