0001-plpgsql-Return-parse-result-not-via-global-variable.patch

text/plain

Filename: 0001-plpgsql-Return-parse-result-not-via-global-variable.patch
Type: text/plain
Part: 0
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 0001
Subject: plpgsql: Return parse result not via global variable
File+
src/pl/plpgsql/src/nls.mk 1 1
src/pl/plpgsql/src/pl_comp.c 2 6
src/pl/plpgsql/src/pl_gram.y 43 42
src/pl/plpgsql/src/plpgsql.h 2 4
src/pl/plpgsql/src/pl_scanner.c 4 1
From da9afde1eea45881749ea832e7ded4491f756272 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Fri, 17 Jan 2025 12:59:56 +0100
Subject: [PATCH 1/4] plpgsql: Return parse result not via global variable

Instead of passing the parse result from yyparse() via a global
variable, pass it via a function output argument.
---
 src/pl/plpgsql/src/nls.mk       |  2 +-
 src/pl/plpgsql/src/pl_comp.c    |  8 +---
 src/pl/plpgsql/src/pl_gram.y    | 85 +++++++++++++++++----------------
 src/pl/plpgsql/src/pl_scanner.c |  5 +-
 src/pl/plpgsql/src/plpgsql.h    |  6 +--
 5 files changed, 52 insertions(+), 54 deletions(-)

diff --git a/src/pl/plpgsql/src/nls.mk b/src/pl/plpgsql/src/nls.mk
index eb06336675c..dd1c1e1440f 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:3 plpgsql_yyerror:3
+GETTEXT_TRIGGERS = $(BACKEND_COMMON_GETTEXT_TRIGGERS) yyerror:4 plpgsql_yyerror:4
 GETTEXT_FLAGS    = $(BACKEND_COMMON_GETTEXT_FLAGS)
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 9dc8218292d..a2de0880fb7 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -38,8 +38,6 @@
  * Our own local and global variables
  * ----------
  */
-PLpgSQL_stmt_block *plpgsql_parse_result;
-
 static int	datums_alloc;
 int			plpgsql_nDatums;
 PLpgSQL_datum **plpgsql_Datums;
@@ -787,10 +785,9 @@ do_compile(FunctionCallInfo fcinfo,
 	/*
 	 * Now parse the function's text
 	 */
-	parse_rc = plpgsql_yyparse(scanner);
+	parse_rc = plpgsql_yyparse(&function->action, scanner);
 	if (parse_rc != 0)
 		elog(ERROR, "plpgsql parser returned %d", parse_rc);
-	function->action = plpgsql_parse_result;
 
 	plpgsql_scanner_finish(scanner);
 	pfree(proc_source);
@@ -945,10 +942,9 @@ plpgsql_compile_inline(char *proc_source)
 	/*
 	 * Now parse the function's text
 	 */
-	parse_rc = plpgsql_yyparse(scanner);
+	parse_rc = plpgsql_yyparse(&function->action, scanner);
 	if (parse_rc != 0)
 		elog(ERROR, "plpgsql parser returned %d", parse_rc);
-	function->action = plpgsql_parse_result;
 
 	plpgsql_scanner_finish(scanner);
 
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index 063ed81f053..64d2c362bf9 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -116,6 +116,7 @@ static	void			check_raise_parameters(PLpgSQL_stmt_raise *stmt);
 
 %}
 
+%parse-param {PLpgSQL_stmt_block **plpgsql_parse_result_p}
 %parse-param {yyscan_t yyscanner}
 %lex-param   {yyscan_t yyscanner}
 %pure-parser
@@ -368,7 +369,7 @@ static	void			check_raise_parameters(PLpgSQL_stmt_raise *stmt);
 
 pl_function		: comp_options pl_block opt_semi
 					{
-						plpgsql_parse_result = (PLpgSQL_stmt_block *) $2;
+						*plpgsql_parse_result_p = (PLpgSQL_stmt_block *) $2;
 						(void) yynerrs;		/* suppress compiler warning */
 					}
 				;
@@ -712,7 +713,7 @@ decl_varname	: T_WORD
 						if (plpgsql_ns_lookup(plpgsql_ns_top(), true,
 											  $1.ident, NULL, NULL,
 											  NULL) != NULL)
-							yyerror(&yylloc, yyscanner, "duplicate declaration");
+							yyerror(&yylloc, NULL, yyscanner, "duplicate declaration");
 
 						if (plpgsql_curr_compile->extra_warnings & PLPGSQL_XCHECK_SHADOWVAR ||
 							plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_SHADOWVAR)
@@ -740,7 +741,7 @@ decl_varname	: T_WORD
 						if (plpgsql_ns_lookup(plpgsql_ns_top(), true,
 											  $1, NULL, NULL,
 											  NULL) != NULL)
-							yyerror(&yylloc, yyscanner, "duplicate declaration");
+							yyerror(&yylloc, NULL, yyscanner, "duplicate declaration");
 
 						if (plpgsql_curr_compile->extra_warnings & PLPGSQL_XCHECK_SHADOWVAR ||
 							plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_SHADOWVAR)
@@ -1143,7 +1144,7 @@ getdiag_item :
 												K_RETURNED_SQLSTATE, "returned_sqlstate"))
 							$$ = PLPGSQL_GETDIAG_RETURNED_SQLSTATE;
 						else
-							yyerror(&yylloc, yyscanner, "unrecognized GET DIAGNOSTICS item");
+							yyerror(&yylloc, NULL, yyscanner, "unrecognized GET DIAGNOSTICS item");
 					}
 				;
 
@@ -1777,7 +1778,7 @@ stmt_return		: K_RETURN
 
 						tok = yylex(&yylval, &yylloc, yyscanner);
 						if (tok == 0)
-							yyerror(&yylloc, yyscanner, "unexpected end of function definition");
+							yyerror(&yylloc, NULL, yyscanner, "unexpected end of function definition");
 
 						if (tok_is_keyword(tok, &yylval,
 										   K_NEXT, "next"))
@@ -1815,7 +1816,7 @@ stmt_raise		: K_RAISE
 
 						tok = yylex(&yylval, &yylloc, yyscanner);
 						if (tok == 0)
-							yyerror(&yylloc, yyscanner, "unexpected end of function definition");
+							yyerror(&yylloc, NULL, yyscanner, "unexpected end of function definition");
 
 						/*
 						 * We could have just RAISE, meaning to re-throw
@@ -1863,7 +1864,7 @@ stmt_raise		: K_RAISE
 								tok = yylex(&yylval, &yylloc, yyscanner);
 							}
 							if (tok == 0)
-								yyerror(&yylloc, yyscanner, "unexpected end of function definition");
+								yyerror(&yylloc, NULL, yyscanner, "unexpected end of function definition");
 
 							/*
 							 * Next we can have a condition name, or
@@ -1883,7 +1884,7 @@ stmt_raise		: K_RAISE
 								 */
 								tok = yylex(&yylval, &yylloc, yyscanner);
 								if (tok != ',' && tok != ';' && tok != K_USING)
-									yyerror(&yylloc, yyscanner, "syntax error");
+									yyerror(&yylloc, NULL, yyscanner, "syntax error");
 
 								while (tok == ',')
 								{
@@ -1908,13 +1909,13 @@ stmt_raise		: K_RAISE
 									char	   *sqlstatestr;
 
 									if (yylex(&yylval, &yylloc, yyscanner) != SCONST)
-										yyerror(&yylloc, yyscanner, "syntax error");
+										yyerror(&yylloc, NULL, yyscanner, "syntax error");
 									sqlstatestr = yylval.str;
 
 									if (strlen(sqlstatestr) != 5)
-										yyerror(&yylloc, yyscanner, "invalid SQLSTATE code");
+										yyerror(&yylloc, NULL, yyscanner, "invalid SQLSTATE code");
 									if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5)
-										yyerror(&yylloc, yyscanner, "invalid SQLSTATE code");
+										yyerror(&yylloc, NULL, yyscanner, "invalid SQLSTATE code");
 									new->condname = sqlstatestr;
 								}
 								else
@@ -1924,13 +1925,13 @@ stmt_raise		: K_RAISE
 									else if (plpgsql_token_is_unreserved_keyword(tok))
 										new->condname = pstrdup(yylval.keyword);
 									else
-										yyerror(&yylloc, yyscanner, "syntax error");
+										yyerror(&yylloc, NULL, yyscanner, "syntax error");
 									plpgsql_recognize_err_condition(new->condname,
 																	false);
 								}
 								tok = yylex(&yylval, &yylloc, yyscanner);
 								if (tok != ';' && tok != K_USING)
-									yyerror(&yylloc, yyscanner, "syntax error");
+									yyerror(&yylloc, NULL, yyscanner, "syntax error");
 							}
 
 							if (tok == K_USING)
@@ -2056,7 +2057,7 @@ stmt_dynexecute : K_EXECUTE
 							if (endtoken == K_INTO)
 							{
 								if (new->into)			/* multiple INTO */
-									yyerror(&yylloc, yyscanner, "syntax error");
+									yyerror(&yylloc, NULL, yyscanner, "syntax error");
 								new->into = true;
 								read_into_target(&new->target, &new->strict, &yylval, &yylloc, yyscanner);
 								endtoken = yylex(&yylval, &yylloc, yyscanner);
@@ -2064,7 +2065,7 @@ stmt_dynexecute : K_EXECUTE
 							else if (endtoken == K_USING)
 							{
 								if (new->params)		/* multiple USING */
-									yyerror(&yylloc, yyscanner, "syntax error");
+									yyerror(&yylloc, NULL, yyscanner, "syntax error");
 								do
 								{
 									expr = read_sql_construct(',', ';', K_INTO,
@@ -2079,7 +2080,7 @@ stmt_dynexecute : K_EXECUTE
 							else if (endtoken == ';')
 								break;
 							else
-								yyerror(&yylloc, yyscanner, "syntax error");
+								yyerror(&yylloc, NULL, yyscanner, "syntax error");
 						}
 
 						$$ = (PLpgSQL_stmt *) new;
@@ -2122,7 +2123,7 @@ stmt_open		: K_OPEN cursor_variable
 							}
 
 							if (tok != K_FOR)
-								yyerror(&yylloc, yyscanner, "syntax error, expected \"FOR\"");
+								yyerror(&yylloc, NULL, yyscanner, "syntax error, expected \"FOR\"");
 
 							tok = yylex(&yylval, &yylloc, yyscanner);
 							if (tok == K_EXECUTE)
@@ -2174,7 +2175,7 @@ stmt_fetch		: K_FETCH opt_fetch_direction cursor_variable K_INTO
 						read_into_target(&target, NULL, &yylval, &yylloc, yyscanner);
 
 						if (yylex(&yylval, &yylloc, yyscanner) != ';')
-							yyerror(&yylloc, yyscanner, "syntax error");
+							yyerror(&yylloc, NULL, yyscanner, "syntax error");
 
 						/*
 						 * We don't allow multiple rows in PL/pgSQL's FETCH
@@ -2398,13 +2399,13 @@ proc_condition	: any_identifier
 
 								/* next token should be a string literal */
 								if (yylex(&yylval, &yylloc, yyscanner) != SCONST)
-									yyerror(&yylloc, yyscanner, "syntax error");
+									yyerror(&yylloc, NULL, yyscanner, "syntax error");
 								sqlstatestr = yylval.str;
 
 								if (strlen(sqlstatestr) != 5)
-									yyerror(&yylloc, yyscanner, "invalid SQLSTATE code");
+									yyerror(&yylloc, NULL, yyscanner, "invalid SQLSTATE code");
 								if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5)
-									yyerror(&yylloc, yyscanner, "invalid SQLSTATE code");
+									yyerror(&yylloc, NULL, yyscanner, "invalid SQLSTATE code");
 
 								new = palloc(sizeof(PLpgSQL_condition));
 								new->sqlerrstate =
@@ -2488,7 +2489,7 @@ any_identifier	: T_WORD
 				| T_DATUM
 					{
 						if ($1.ident == NULL) /* composite name not OK */
-							yyerror(&yylloc, yyscanner, "syntax error");
+							yyerror(&yylloc, NULL, yyscanner, "syntax error");
 						$$ = $1.ident;
 					}
 				;
@@ -2647,7 +2648,7 @@ current_token_is_not_variable(int tok, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yysca
 	else if (tok == T_CWORD)
 		cword_is_not_variable(&(yylvalp->cword), *yyllocp, yyscanner);
 	else
-		yyerror(yyllocp, yyscanner, "syntax error");
+		yyerror(yyllocp, NULL, yyscanner, "syntax error");
 }
 
 /* Convenience routine to read an expression with one possible terminator */
@@ -2739,7 +2740,7 @@ read_sql_construct(int until,
 		{
 			parenlevel--;
 			if (parenlevel < 0)
-				yyerror(yyllocp, yyscanner, "mismatched parentheses");
+				yyerror(yyllocp, NULL, yyscanner, "mismatched parentheses");
 		}
 
 		/*
@@ -2750,7 +2751,7 @@ read_sql_construct(int until,
 		if (tok == 0 || tok == ';')
 		{
 			if (parenlevel != 0)
-				yyerror(yyllocp, yyscanner, "mismatched parentheses");
+				yyerror(yyllocp, NULL, yyscanner, "mismatched parentheses");
 			if (isexpression)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
@@ -2779,9 +2780,9 @@ read_sql_construct(int until,
 	if (startlocation >= endlocation)
 	{
 		if (isexpression)
-			yyerror(yyllocp, yyscanner, "missing expression");
+			yyerror(yyllocp, NULL, yyscanner, "missing expression");
 		else
-			yyerror(yyllocp, yyscanner, "missing SQL statement");
+			yyerror(yyllocp, NULL, yyscanner, "missing SQL statement");
 	}
 
 	/*
@@ -2910,7 +2911,7 @@ read_datatype(int tok, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
 			if (tok == ICONST)
 				tok = yylex(yylvalp, yyllocp, yyscanner);
 			if (tok != ']')
-				yyerror(yyllocp, yyscanner, "syntax error, expected \"]\"");
+				yyerror(yyllocp, NULL, yyscanner, "syntax error, expected \"]\"");
 			tok = yylex(yylvalp, yyllocp, yyscanner);
 		}
 		plpgsql_push_back_token(tok, yylvalp, yyllocp, yyscanner);
@@ -2932,9 +2933,9 @@ read_datatype(int tok, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
 		if (tok == 0)
 		{
 			if (parenlevel != 0)
-				yyerror(yyllocp, yyscanner, "mismatched parentheses");
+				yyerror(yyllocp, NULL, yyscanner, "mismatched parentheses");
 			else
-				yyerror(yyllocp, yyscanner, "incomplete data type declaration");
+				yyerror(yyllocp, NULL, yyscanner, "incomplete data type declaration");
 		}
 		/* Possible followers for datatype in a declaration */
 		if (tok == K_COLLATE || tok == K_NOT ||
@@ -2957,7 +2958,7 @@ read_datatype(int tok, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
 	type_name = ds.data;
 
 	if (type_name[0] == '\0')
-		yyerror(yyllocp, yyscanner, "missing data type declaration");
+		yyerror(yyllocp, NULL, yyscanner, "missing data type declaration");
 
 	result = parse_datatype(type_name, startlocation, yyscanner);
 
@@ -3082,7 +3083,7 @@ make_execsql_stmt(int firsttoken, int location, PLword *word, YYSTYPE *yylvalp,
 		if (tok == ';' && paren_depth == 0 && begin_depth == 0)
 			break;
 		if (tok == 0)
-			yyerror(yyllocp, yyscanner, "unexpected end of function definition");
+			yyerror(yyllocp, NULL, yyscanner, "unexpected end of function definition");
 		if (tok == K_INTO)
 		{
 			if (prev_tok == K_INSERT)
@@ -3092,7 +3093,7 @@ make_execsql_stmt(int firsttoken, int location, PLword *word, YYSTYPE *yylvalp,
 			if (firsttoken == K_IMPORT)
 				continue;		/* IMPORT ... INTO is not an INTO-target */
 			if (have_into)
-				yyerror(yyllocp, yyscanner, "INTO specified more than once");
+				yyerror(yyllocp, NULL, yyscanner, "INTO specified more than once");
 			have_into = true;
 			into_start_loc = *yyllocp;
 			plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL;
@@ -3170,7 +3171,7 @@ read_fetch_direction(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
 
 	tok = yylex(yylvalp, yyllocp, yyscanner);
 	if (tok == 0)
-		yyerror(yyllocp, yyscanner, "unexpected end of function definition");
+		yyerror(yyllocp, NULL, yyscanner, "unexpected end of function definition");
 
 	if (tok_is_keyword(tok, yylvalp,
 					   K_NEXT, "next"))
@@ -3262,7 +3263,7 @@ read_fetch_direction(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
 	{
 		tok = yylex(yylvalp, yyllocp, yyscanner);
 		if (tok != K_FROM && tok != K_IN)
-			yyerror(yyllocp, yyscanner, "expected FROM or IN");
+			yyerror(yyllocp, NULL, yyscanner, "expected FROM or IN");
 	}
 
 	return fetch;
@@ -3281,7 +3282,7 @@ complete_direction(PLpgSQL_stmt_fetch *fetch, bool *check_FROM, YYSTYPE *yylvalp
 
 	tok = yylex(yylvalp, yyllocp, yyscanner);
 	if (tok == 0)
-		yyerror(yyllocp, yyscanner, "unexpected end of function definition");
+		yyerror(yyllocp, NULL, yyscanner, "unexpected end of function definition");
 
 	if (tok == K_FROM || tok == K_IN)
 	{
@@ -3882,7 +3883,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until, YYSTYPE *yylvalp, YYLTYPE *yyll
 					 parser_errposition(*yyllocp)));
 
 		if (tok != until)
-			yyerror(yyllocp, yyscanner, "syntax error");
+			yyerror(yyllocp, NULL, yyscanner, "syntax error");
 
 		return NULL;
 	}
@@ -3943,7 +3944,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until, YYSTYPE *yylvalp, YYLTYPE *yyll
 			 */
 			tok2 = yylex(yylvalp, yyllocp, yyscanner);
 			if (tok2 != COLON_EQUALS)
-				yyerror(yyllocp, yyscanner, "syntax error");
+				yyerror(yyllocp, NULL, yyscanner, "syntax error");
 
 			any_named = true;
 		}
@@ -4017,7 +4018,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until, YYSTYPE *yylvalp, YYLTYPE *yyll
 	/* Next we'd better find the until token */
 	tok = yylex(yylvalp, yyllocp, yyscanner);
 	if (tok != until)
-		yyerror(yyllocp, yyscanner, "syntax error");
+		yyerror(yyllocp, NULL, yyscanner, "syntax error");
 
 	return expr;
 }
@@ -4036,7 +4037,7 @@ read_raise_options(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
 		int			tok;
 
 		if ((tok = yylex(yylvalp, yyllocp, yyscanner)) == 0)
-			yyerror(yyllocp, yyscanner, "unexpected end of function definition");
+			yyerror(yyllocp, NULL, yyscanner, "unexpected end of function definition");
 
 		opt = (PLpgSQL_raise_option *) palloc(sizeof(PLpgSQL_raise_option));
 
@@ -4068,11 +4069,11 @@ read_raise_options(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
 								K_SCHEMA, "schema"))
 			opt->opt_type = PLPGSQL_RAISEOPTION_SCHEMA;
 		else
-			yyerror(yyllocp, yyscanner, "unrecognized RAISE statement option");
+			yyerror(yyllocp, NULL, yyscanner, "unrecognized RAISE statement option");
 
 		tok = yylex(yylvalp, yyllocp, yyscanner);
 		if (tok != '=' && tok != COLON_EQUALS)
-			yyerror(yyllocp, yyscanner, "syntax error, expected \"=\"");
+			yyerror(yyllocp, NULL, yyscanner, "syntax error, expected \"=\"");
 
 		opt->expr = read_sql_expression2(',', ';', ", or ;", &tok, yylvalp, yyllocp, yyscanner);
 
diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c
index 11d79a8a683..d08187dafcb 100644
--- a/src/pl/plpgsql/src/pl_scanner.c
+++ b/src/pl/plpgsql/src/pl_scanner.c
@@ -526,9 +526,12 @@ plpgsql_scanner_errposition(int location, yyscan_t yyscanner)
  * parsers report error as soon as the first unparsable token is reached.
  * Beware of using yyerror for other purposes, as the cursor position might
  * be misleading!
+ *
+ * (The second argument is enforced by Bison to match the second argument of
+ * yyparse(), but it is not used here.)
  */
 void
-plpgsql_yyerror(YYLTYPE *yyllocp, yyscan_t yyscanner, const char *message)
+plpgsql_yyerror(YYLTYPE *yyllocp, PLpgSQL_stmt_block **plpgsql_parse_result_p, yyscan_t yyscanner, const char *message)
 {
 	char	   *yytext = yyextra->core_yy_extra.scanbuf + *yyllocp;
 
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index c3ce4161a32..441df5354e2 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -1212,8 +1212,6 @@ extern int	plpgsql_extra_errors;
 extern bool plpgsql_check_syntax;
 extern bool plpgsql_DumpExecTree;
 
-extern PLpgSQL_stmt_block *plpgsql_parse_result;
-
 extern int	plpgsql_nDatums;
 extern PLpgSQL_datum **plpgsql_Datums;
 
@@ -1332,7 +1330,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, yyscan_t yyscanner);
-extern void plpgsql_yyerror(YYLTYPE *yyllocp, yyscan_t yyscanner, const char *message) pg_attribute_noreturn();
+extern void plpgsql_yyerror(YYLTYPE *yyllocp, PLpgSQL_stmt_block **plpgsql_parse_result_p, yyscan_t yyscanner, const char *message) pg_attribute_noreturn();
 extern int	plpgsql_location_to_lineno(int location, yyscan_t yyscanner);
 extern int	plpgsql_latest_lineno(yyscan_t yyscanner);
 extern yyscan_t plpgsql_scanner_init(const char *str);
@@ -1341,6 +1339,6 @@ extern void plpgsql_scanner_finish(yyscan_t yyscanner);
 /*
  * Externs in pl_gram.y
  */
-extern int	plpgsql_yyparse(yyscan_t yyscanner);
+extern int	plpgsql_yyparse(PLpgSQL_stmt_block **plpgsql_parse_result_p, yyscan_t yyscanner);
 
 #endif							/* PLPGSQL_H */

base-commit: 43830ecb8a9b6a1bc322298a77a5e0d87c2e83d4
-- 
2.47.1