From da9afde1eea45881749ea832e7ded4491f756272 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut 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