Here are some more patches. This should cover the last sub-topic of this topic: not passing the yyparse() result via global variables. This uses techniques that are already in use in some parsers in the tree, for example cube and jsonpath. The syncrep parser was a bit trickier, because there we need to pass the syncrep_parse_error variable all the way down to the scanner (not just the parser), but overall it's all still pretty compact and standard.
From da9afde1eea45881749ea832e7ded4491f756272 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@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

From 5925f806205ae9f868fb732da24e529ee67ea87d Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@eisentraut.org>
Date: Fri, 17 Jan 2025 13:14:16 +0100
Subject: [PATCH 2/4] pgbench: 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/bin/pgbench/exprparse.y | 5 ++---
 src/bin/pgbench/exprscan.l  | 6 +++++-
 src/bin/pgbench/pgbench.c   | 4 +---
 src/bin/pgbench/pgbench.h   | 6 ++----
 4 files changed, 10 insertions(+), 11 deletions(-)

diff --git a/src/bin/pgbench/exprparse.y b/src/bin/pgbench/exprparse.y
index 9b5ab8074eb..68a37e49e45 100644
--- a/src/bin/pgbench/exprparse.y
+++ b/src/bin/pgbench/exprparse.y
@@ -21,8 +21,6 @@
 #define PGBENCH_NARGS_HASH             (-3)
 #define PGBENCH_NARGS_PERMUTE  (-4)
 
-PgBenchExpr *expr_parse_result;
-
 static PgBenchExprList *make_elist(PgBenchExpr *expr, PgBenchExprList *list);
 static PgBenchExpr *make_null_constant(void);
 static PgBenchExpr *make_boolean_constant(bool bval);
@@ -42,6 +40,7 @@ static PgBenchExpr *make_case(yyscan_t yyscanner, 
PgBenchExprList *when_then_lis
 %expect 0
 %name-prefix="expr_yy"
 
+%parse-param {PgBenchExpr **expr_parse_result_p}
 %parse-param {yyscan_t yyscanner}
 %lex-param   {yyscan_t yyscanner}
 
@@ -81,7 +80,7 @@ static PgBenchExpr *make_case(yyscan_t yyscanner, 
PgBenchExprList *when_then_lis
 %%
 
 result: expr                           {
-                                                               
expr_parse_result = $1;
+                                                               
*expr_parse_result_p = $1;
                                                                (void) yynerrs; 
/* suppress compiler warning */
                                                        }
 
diff --git a/src/bin/pgbench/exprscan.l b/src/bin/pgbench/exprscan.l
index 46f6ea05121..8943a52e9f0 100644
--- a/src/bin/pgbench/exprscan.l
+++ b/src/bin/pgbench/exprscan.l
@@ -296,8 +296,12 @@ expr_yyerror_more(yyscan_t yyscanner, const char *message, 
const char *more)
                                 message, more, error_detection_offset - 
expr_start_offset);
 }
 
+/*
+ * (The first argument is enforced by Bison to match the first argument of
+ * yyparse(), but it is not used here.)
+ */
 void
-expr_yyerror(yyscan_t yyscanner, const char *message)
+expr_yyerror(PgBenchExpr **expr_parse_result_p, yyscan_t yyscanner, const char 
*message)
 {
        expr_yyerror_more(yyscanner, message, NULL);
 }
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index c415e0f32c1..40592e62606 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -5706,14 +5706,12 @@ process_backslash_command(PsqlScanState sstate, const 
char *source)
                yyscanner = expr_scanner_init(sstate, source, lineno, 
start_offset,
                                                                          
my_command->argv[0]);
 
-               if (expr_yyparse(yyscanner) != 0)
+               if (expr_yyparse(&my_command->expr, yyscanner) != 0)
                {
                        /* dead code: exit done from syntax_error called by 
yyerror */
                        exit(1);
                }
 
-               my_command->expr = expr_parse_result;
-
                /* Save line, trimming any trailing newline */
                my_command->first_line =
                        expr_scanner_get_substring(sstate,
diff --git a/src/bin/pgbench/pgbench.h b/src/bin/pgbench/pgbench.h
index 4b607b7ee04..f6a883611c5 100644
--- a/src/bin/pgbench/pgbench.h
+++ b/src/bin/pgbench/pgbench.h
@@ -138,11 +138,9 @@ struct PgBenchExprList
        PgBenchExprLink *tail;
 };
 
-extern PgBenchExpr *expr_parse_result;
-
-extern int     expr_yyparse(yyscan_t yyscanner);
+extern int     expr_yyparse(PgBenchExpr **expr_parse_result_p, yyscan_t 
yyscanner);
 extern int     expr_yylex(union YYSTYPE *yylval_param, yyscan_t yyscanner);
-extern void expr_yyerror(yyscan_t yyscanner, const char *message) 
pg_attribute_noreturn();
+extern void expr_yyerror(PgBenchExpr **expr_parse_result_p, yyscan_t 
yyscanner, const char *message) pg_attribute_noreturn();
 extern void expr_yyerror_more(yyscan_t yyscanner, const char *message,
                                                          const char *more) 
pg_attribute_noreturn();
 extern bool expr_lex_one_word(PsqlScanState state, PQExpBuffer word_buf,
-- 
2.47.1

From 0d2703ecae444499a179af7c5cb6e9001c44005d Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@eisentraut.org>
Date: Fri, 17 Jan 2025 13:18:38 +0100
Subject: [PATCH 3/4] replication parser: 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/backend/nls.mk                          |  2 +-
 src/backend/replication/repl_gram.y         |  7 ++-----
 src/backend/replication/repl_scanner.l      | 10 +++++++---
 src/backend/replication/walsender.c         |  4 +---
 src/include/replication/walsender_private.h |  6 ++----
 5 files changed, 13 insertions(+), 16 deletions(-)

diff --git a/src/backend/nls.mk b/src/backend/nls.mk
index 5a5298b33f7..210893c7b72 100644
--- a/src/backend/nls.mk
+++ b/src/backend/nls.mk
@@ -9,7 +9,7 @@ GETTEXT_TRIGGERS = $(BACKEND_COMMON_GETTEXT_TRIGGERS) \
                    yyerror \
                    jsonpath_yyerror:4 \
                    parser_yyerror \
-                   replication_yyerror:2 \
+                   replication_yyerror:3 \
                    scanner_yyerror \
                    syncrep_yyerror:2 \
                    report_invalid_record:2 \
diff --git a/src/backend/replication/repl_gram.y 
b/src/backend/replication/repl_gram.y
index 61e68d0727a..7440aae5a1a 100644
--- a/src/backend/replication/repl_gram.y
+++ b/src/backend/replication/repl_gram.y
@@ -25,10 +25,6 @@
 #include "repl_gram.h"
 
 
-/* Result of the parsing is returned here */
-Node *replication_parse_result;
-
-
 /*
  * Bison doesn't allocate anything that needs to live across parser calls,
  * so we can easily have it use palloc instead of malloc.  This prevents
@@ -39,6 +35,7 @@ Node *replication_parse_result;
 
 %}
 
+%parse-param {Node **replication_parse_result_p}
 %parse-param {yyscan_t yyscanner}
 %lex-param   {yyscan_t yyscanner}
 %pure-parser
@@ -104,7 +101,7 @@ Node *replication_parse_result;
 
 firstcmd: command opt_semicolon
                                {
-                                       replication_parse_result = $1;
+                                       *replication_parse_result_p = $1;
 
                                        (void) yynerrs; /* suppress compiler 
warning */
                                }
diff --git a/src/backend/replication/repl_scanner.l 
b/src/backend/replication/repl_scanner.l
index de10cb5abd1..014ea8d25c6 100644
--- a/src/backend/replication/repl_scanner.l
+++ b/src/backend/replication/repl_scanner.l
@@ -156,7 +156,7 @@ UPLOAD_MANIFEST             { return K_UPLOAD_MANIFEST; }
                                        uint32  hi,
                                                        lo;
                                        if (sscanf(yytext, "%X/%X", &hi, &lo) 
!= 2)
-                                               replication_yyerror(yyscanner, 
"invalid streaming start location");
+                                               replication_yyerror(NULL, 
yyscanner, "invalid streaming start location");
                                        yylval->recptr = ((uint64) hi) << 32 | 
lo;
                                        return RECPTR;
                                }
@@ -213,7 +213,7 @@ UPLOAD_MANIFEST             { return K_UPLOAD_MANIFEST; }
                                        return yytext[0];
                                }
 
-<xq,xd><<EOF>> { replication_yyerror(yyscanner, "unterminated quoted string"); 
}
+<xq,xd><<EOF>> { replication_yyerror(NULL, yyscanner, "unterminated quoted 
string"); }
 
 
 <<EOF>>                        {
@@ -252,8 +252,12 @@ addlitchar(unsigned char ychar, yyscan_t yyscanner)
        appendStringInfoChar(&yyextra->litbuf, ychar);
 }
 
+/*
+  * (The first argument is enforced by Bison to match the first argument of
+  * yyparse(), but it is not used here.)
+  */
 void
-replication_yyerror(yyscan_t yyscanner, const char *message)
+replication_yyerror(Node **replication_parse_result_p, yyscan_t yyscanner, 
const char *message)
 {
        ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
diff --git a/src/backend/replication/walsender.c 
b/src/backend/replication/walsender.c
index a0782b1bbf6..bac504b554e 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -2017,7 +2017,7 @@ exec_replication_command(const char *cmd_string)
        /*
         * Looks like a WalSender command, so parse it.
         */
-       parse_rc = replication_yyparse(scanner);
+       parse_rc = replication_yyparse(&cmd_node, scanner);
        if (parse_rc != 0)
                ereport(ERROR,
                                (errcode(ERRCODE_SYNTAX_ERROR),
@@ -2025,8 +2025,6 @@ exec_replication_command(const char *cmd_string)
                                                                 parse_rc)));
        replication_scanner_finish(scanner);
 
-       cmd_node = replication_parse_result;
-
        /*
         * Report query to various monitoring facilities.  For this purpose, we
         * report replication commands just like SQL commands.
diff --git a/src/include/replication/walsender_private.h 
b/src/include/replication/walsender_private.h
index 5ab96ed5f7d..814b812432a 100644
--- a/src/include/replication/walsender_private.h
+++ b/src/include/replication/walsender_private.h
@@ -130,13 +130,11 @@ union YYSTYPE;
 #define YY_TYPEDEF_YY_SCANNER_T
 typedef void *yyscan_t;
 #endif
-extern int     replication_yyparse(yyscan_t yyscanner);
+extern int     replication_yyparse(Node **replication_parse_result_p, yyscan_t 
yyscanner);
 extern int     replication_yylex(union YYSTYPE *yylval_param, yyscan_t 
yyscanner);
-extern void replication_yyerror(yyscan_t yyscanner, const char *message) 
pg_attribute_noreturn();
+extern void replication_yyerror(Node **replication_parse_result_p, yyscan_t 
yyscanner, const char *message) pg_attribute_noreturn();
 extern void replication_scanner_init(const char *str, yyscan_t *yyscannerp);
 extern void replication_scanner_finish(yyscan_t yyscanner);
 extern bool replication_scanner_is_replication_command(yyscan_t yyscanner);
 
-extern PGDLLIMPORT Node *replication_parse_result;
-
 #endif                                                 /* _WALSENDER_PRIVATE_H 
*/
-- 
2.47.1

From 4d8bc0e8ad06b1e9edecdd682ddf17dc6800a4dd Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@eisentraut.org>
Date: Fri, 17 Jan 2025 15:41:11 +0100
Subject: [PATCH 4/4] syncrep parser: 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/backend/nls.mk                        |  2 +-
 src/backend/replication/syncrep.c         |  8 ++++----
 src/backend/replication/syncrep_gram.y    |  9 ++++-----
 src/backend/replication/syncrep_scanner.l | 22 +++++++++++++++++++---
 src/include/replication/syncrep.h         | 10 +++-------
 5 files changed, 31 insertions(+), 20 deletions(-)

diff --git a/src/backend/nls.mk b/src/backend/nls.mk
index 210893c7b72..b7d5dd46e45 100644
--- a/src/backend/nls.mk
+++ b/src/backend/nls.mk
@@ -11,7 +11,7 @@ GETTEXT_TRIGGERS = $(BACKEND_COMMON_GETTEXT_TRIGGERS) \
                    parser_yyerror \
                    replication_yyerror:3 \
                    scanner_yyerror \
-                   syncrep_yyerror:2 \
+                   syncrep_yyerror:4 \
                    report_invalid_record:2 \
                    ereport_startup_progress \
                    json_token_error:2 \
diff --git a/src/backend/replication/syncrep.c 
b/src/backend/replication/syncrep.c
index 1ce8bc7533f..d75e3968035 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -996,13 +996,13 @@ check_synchronous_standby_names(char **newval, void 
**extra, GucSource source)
                int                     parse_rc;
                SyncRepConfigData *pconf;
 
-               /* Reset communication variables to ensure a fresh start */
-               syncrep_parse_result = NULL;
-               syncrep_parse_error_msg = NULL;
+               /* Result of parsing is returned in one of these two variables 
*/
+               SyncRepConfigData *syncrep_parse_result = NULL;
+               char       *syncrep_parse_error_msg = NULL;
 
                /* Parse the synchronous_standby_names string */
                syncrep_scanner_init(*newval, &scanner);
-               parse_rc = syncrep_yyparse(scanner);
+               parse_rc = syncrep_yyparse(&syncrep_parse_result, 
&syncrep_parse_error_msg, scanner);
                syncrep_scanner_finish(scanner);
 
                if (parse_rc != 0 || syncrep_parse_result == NULL)
diff --git a/src/backend/replication/syncrep_gram.y 
b/src/backend/replication/syncrep_gram.y
index 1a3b89ca674..22297bb151a 100644
--- a/src/backend/replication/syncrep_gram.y
+++ b/src/backend/replication/syncrep_gram.y
@@ -19,10 +19,6 @@
 
 #include "syncrep_gram.h"
 
-/* Result of parsing is returned in one of these two variables */
-SyncRepConfigData *syncrep_parse_result;
-char      *syncrep_parse_error_msg;
-
 static SyncRepConfigData *create_syncrep_config(const char *num_sync,
                                        List *members, uint8 syncrep_method);
 
@@ -36,7 +32,10 @@ static SyncRepConfigData *create_syncrep_config(const char 
*num_sync,
 
 %}
 
+%parse-param {SyncRepConfigData **syncrep_parse_result_p}
+%parse-param {char **syncrep_parse_error_msg_p}
 %parse-param {yyscan_t yyscanner}
+%lex-param   {char **syncrep_parse_error_msg_p}
 %lex-param   {yyscan_t yyscanner}
 %pure-parser
 %expect 0
@@ -60,7 +59,7 @@ static SyncRepConfigData *create_syncrep_config(const char 
*num_sync,
 %%
 result:
                standby_config                          {
-                                                                               
syncrep_parse_result = $1;
+                                                                               
*syncrep_parse_result_p = $1;
                                                                                
(void) yynerrs; /* suppress compiler warning */
                                                                        }
        ;
diff --git a/src/backend/replication/syncrep_scanner.l 
b/src/backend/replication/syncrep_scanner.l
index 05e5fdecf1b..7dec1f869c7 100644
--- a/src/backend/replication/syncrep_scanner.l
+++ b/src/backend/replication/syncrep_scanner.l
@@ -42,6 +42,13 @@ struct syncrep_yy_extra_type
        StringInfoData xdbuf;
 };
 
+/*
+ * Better keep this definition here than put it in replication/syncrep.h and
+ * save a bit of duplication.  Putting it in replication/syncrep.h would leak
+ * the definition to other parts and possibly affect other scanners.
+*/
+#define YY_DECL extern int     syncrep_yylex(union YYSTYPE *yylval_param, char 
**syncrep_parse_error_msg_p, yyscan_t yyscanner)
+
 /* LCOV_EXCL_START */
 
 %}
@@ -104,7 +111,7 @@ xdinside            [^"]+
                                return NAME;
                }
 <xd><<EOF>> {
-                               syncrep_yyerror(yyscanner, "unterminated quoted 
identifier");
+                               syncrep_yyerror(NULL, 
syncrep_parse_error_msg_p, yyscanner, "unterminated quoted identifier");
                                return JUNK;
                }
 
@@ -136,12 +143,21 @@ xdinside          [^"]+
 #undef yyextra
 #define yyextra (((struct yyguts_t *) yyscanner)->yyextra_r)
 
-/* Needs to be here for access to yytext */
+/*
+ * This yyerror() function does not raise an error (elog or similar), it just
+ * collects the error message in *syncrep_parse_error_msg_p and leaves it to
+ * the ultimate caller of the syncrep parser to raise the error.  (The
+ * ultimate caller will do that with special GUC error functions.)
+ *
+ * (The first argument is enforced by Bison to match the first argument of
+ * yyparse(), but it is not used here.)
+ */
 void
-syncrep_yyerror(yyscan_t yyscanner, const char *message)
+syncrep_yyerror(SyncRepConfigData **syncrep_parse_result_p, char 
**syncrep_parse_error_msg_p, yyscan_t yyscanner, const char *message)
 {
        struct yyguts_t *yyg = (struct yyguts_t *) yyscanner;   /* needed for 
yytext
                                                                                
                                         * macro */
+       char *syncrep_parse_error_msg = *syncrep_parse_error_msg_p;
 
        /* report only the first error in a parse operation */
        if (syncrep_parse_error_msg)
diff --git a/src/include/replication/syncrep.h 
b/src/include/replication/syncrep.h
index 33b9cdb18f7..675669a79f7 100644
--- a/src/include/replication/syncrep.h
+++ b/src/include/replication/syncrep.h
@@ -73,10 +73,6 @@ typedef struct SyncRepConfigData
 
 extern PGDLLIMPORT SyncRepConfigData *SyncRepConfig;
 
-/* communication variables for parsing synchronous_standby_names GUC */
-extern PGDLLIMPORT SyncRepConfigData *syncrep_parse_result;
-extern PGDLLIMPORT char *syncrep_parse_error_msg;
-
 /* user-settable parameters for synchronous replication */
 extern PGDLLIMPORT char *SyncRepStandbyNames;
 
@@ -105,9 +101,9 @@ union YYSTYPE;
 #define YY_TYPEDEF_YY_SCANNER_T
 typedef void *yyscan_t;
 #endif
-extern int     syncrep_yyparse(yyscan_t yyscanner);
-extern int     syncrep_yylex(union YYSTYPE *yylval_param, yyscan_t yyscanner);
-extern void syncrep_yyerror(yyscan_t yyscanner, const char *str);
+extern int     syncrep_yyparse(SyncRepConfigData **syncrep_parse_result_p, 
char **syncrep_parse_error_msg_p, yyscan_t yyscanner);
+extern int     syncrep_yylex(union YYSTYPE *yylval_param, char 
**syncrep_parse_error_msg_p, yyscan_t yyscanner);
+extern void syncrep_yyerror(SyncRepConfigData **syncrep_parse_result_p, char 
**syncrep_parse_error_msg_p, yyscan_t yyscanner, const char *str);
 extern void syncrep_scanner_init(const char *str, yyscan_t *yyscannerp);
 extern void syncrep_scanner_finish(yyscan_t yyscanner);
 
-- 
2.47.1

Reply via email to