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