diff --git a/src/common/pl/plpgsql/src/gram.y b/src/common/pl/plpgsql/src/gram.y index c1756a4e0..b5e187054 100755 --- a/src/common/pl/plpgsql/src/gram.y +++ b/src/common/pl/plpgsql/src/gram.y @@ -668,7 +668,7 @@ opt_semi : | ';' ; -pl_block : decl_sect begin_decl proc_sect exception_sect K_END opt_label +pl_block : decl_sect K_BEGIN declare_sect proc_sect exception_sect K_END opt_label { PLpgSQL_stmt_block *newp; @@ -681,10 +681,10 @@ pl_block : decl_sect begin_decl proc_sect exception_sect K_END opt_label newp->isAutonomous = $1.isAutonomous; newp->n_initvars = $1.n_initvars; newp->initvarnos = $1.initvarnos; - newp->body = $3; - newp->exceptions = $4; + newp->body = $4; + newp->exceptions = $5; - check_labels($1.label, $6, @6); + check_labels($1.label, $7, @7); plpgsql_ns_pop(); $$ = (PLpgSQL_stmt *)newp; @@ -694,9 +694,20 @@ pl_block : decl_sect begin_decl proc_sect exception_sect K_END opt_label } ; -begin_decl : K_BEGIN - | K_BEGIN declare_stmts - ; +declare_sect : + { + /* done with decls, so resume identifier lookup */ + u_sess->plsql_cxt.curr_compile_context->plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL; + } + | { SetErrorState(); + u_sess->plsql_cxt.curr_compile_context->plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_DECLARE; + } declare_stmts + { + /* done with decls, so resume identifier lookup */ + u_sess->plsql_cxt.curr_compile_context->plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL; + u_sess->plsql_cxt.pragma_autonomous = false; + } + ; declare_stmts : declare_stmts declare_stmt | declare_stmt @@ -709,6 +720,11 @@ declare_stmt : T_DECLARE_CURSOR decl_varname K_CURSOR opt_scrollable } decl_cursor_args decl_is_for decl_cursor_query { + int tok = -1; + plpgsql_peek(&tok); + if (tok != K_DECLARE) { + u_sess->plsql_cxt.curr_compile_context->plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL; + } PLpgSQL_var *newp; /* pop local namespace for cursor args */ @@ -735,6 +751,11 @@ declare_stmt : T_DECLARE_CURSOR decl_varname K_CURSOR opt_scrollable } | T_DECLARE_CONDITION decl_varname K_CONDITION K_FOR condition_value ';' { + int tok = -1; + plpgsql_peek(&tok); + if (tok != K_DECLARE) { + u_sess->plsql_cxt.curr_compile_context->plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL; + } IsInPublicNamespace($2->name); PLpgSQL_var *var; @@ -749,35 +770,31 @@ declare_stmt : T_DECLARE_CURSOR decl_varname K_CURSOR opt_scrollable } ; -condition_value : any_identifier +condition_value : K_SQLSTATE { - if (strcmp($1, "sqlstate") == 0) { - /* next token should be a string literal */ - char *sqlstatestr; - if (yylex() != SCONST) - yyerror("syntax error"); - sqlstatestr = yylval.str; - - if (strlen(sqlstatestr) != 5) - yyerror("invalid SQLSTATE code"); - if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5) - yyerror("invalid SQLSTATE code"); - if (strncmp(sqlstatestr, "00", 2) == 0) { - const char* message = "bad SQLSTATE"; - InsertErrorMessage(message, plpgsql_yylloc); - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION), - errmsg("bad SQLSTATE '%s'",sqlstatestr))); - } - - $$ = MAKE_SQLSTATE(sqlstatestr[0], - sqlstatestr[1], - sqlstatestr[2], - sqlstatestr[3], - sqlstatestr[4]); - } else { + /* next token should be a string literal */ + char *sqlstatestr; + if (yylex() != SCONST) yyerror("syntax error"); + sqlstatestr = yylval.str; + + if (strlen(sqlstatestr) != 5) + yyerror("invalid SQLSTATE code"); + if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5) + yyerror("invalid SQLSTATE code"); + if (strncmp(sqlstatestr, "00", 2) == 0) { + const char* message = "bad SQLSTATE"; + InsertErrorMessage(message, plpgsql_yylloc); + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION), + errmsg("bad SQLSTATE '%s'",sqlstatestr))); } + + $$ = MAKE_SQLSTATE(sqlstatestr[0], + sqlstatestr[1], + sqlstatestr[2], + sqlstatestr[3], + sqlstatestr[4]); } | ICONST { diff --git a/src/common/pl/plpgsql/src/pl_scanner.cpp b/src/common/pl/plpgsql/src/pl_scanner.cpp index 408978ec7..dcf4273f8 100644 --- a/src/common/pl/plpgsql/src/pl_scanner.cpp +++ b/src/common/pl/plpgsql/src/pl_scanner.cpp @@ -669,6 +669,16 @@ void plpgsql_peek2(int* tok1_p, int* tok2_p, int* tok1_loc, int* tok2_loc) push_back_token(tok1, &aux1); } +void plpgsql_peek(int* tok1_p) +{ + int tok1; + TokenAuxData aux1; + + tok1 = internal_yylex(&aux1); + + *tok1_p = tok1; + push_back_token(tok1, &aux1); +} /* * plpgsql_scanner_errposition * Report an error cursor position, if possible. diff --git a/src/include/utils/plpgsql.h b/src/include/utils/plpgsql.h index 59db0470d..ee22eb270 100644 --- a/src/include/utils/plpgsql.h +++ b/src/include/utils/plpgsql.h @@ -1726,6 +1726,7 @@ extern int plpgsql_yylex(void); extern void plpgsql_push_back_token(int token); extern void plpgsql_append_source_text(StringInfo buf, int startlocation, int endlocation); extern void plpgsql_peek2(int* tok1_p, int* tok2_p, int* tok1_loc, int* tok2_loc); +extern void plpgsql_peek(int* tok1_p); extern int plpgsql_scanner_errposition(int location); extern void plpgsql_yyerror(const char* message, bool isError = false); extern int plpgsql_location_to_lineno(int location); diff --git a/src/test/regress/expected/mysql_syntax.out b/src/test/regress/expected/mysql_syntax.out index c70a85614..4edc6fb99 100644 --- a/src/test/regress/expected/mysql_syntax.out +++ b/src/test/regress/expected/mysql_syntax.out @@ -248,6 +248,54 @@ NOTICE: oracle : usa : 2 (1 row) +create or replace procedure test_cursor_1 +as + company_name varchar(100); + company_loc varchar(100); + company_no integer; + +begin + declare c1_all cursor is --cursor without args + select name, loc, no from company order by 1, 2, 3; + declare c1_all cursor is --cursor without args + select name, loc, no from company order by 1, 2, 3; + if not c1_all%isopen then + open c1_all; + end if; + loop + fetch c1_all into company_name, company_loc, company_no; + exit when c1_all%notfound; + raise notice '% : % : %',company_name,company_loc,company_no; + end loop; + if c1_all%isopen then + close c1_all; + end if; +end; +/ +ERROR: duplicate declaration at or near "c1_all" +LINE 8: declare c1_all cursor is --cursor without args + ^ +QUERY: DECLARE company_name varchar(100); + company_loc varchar(100); + company_no integer; + +begin + declare c1_all cursor is --cursor without args + select name, loc, no from company order by 1, 2, 3; + declare c1_all cursor is --cursor without args + select name, loc, no from company order by 1, 2, 3; + if not c1_all%isopen then + open c1_all; + end if; + loop + fetch c1_all into company_name, company_loc, company_no; + exit when c1_all%notfound; + raise notice '% : % : %',company_name,company_loc,company_no; + end loop; + if c1_all%isopen then + close c1_all; + end if; +end -- test declare condition create or replace procedure test_condition_1 as declare @@ -306,7 +354,7 @@ exception END; END; / -ERROR: syntax error at or near "DIVISION_ZERO" +ERROR: duplicate declaration at or near "DIVISION_ZERO" LINE 6: declare DIVISION_ZERO condition for SQLSTATE '22005'; ^ QUERY: diff --git a/src/test/regress/sql/mysql_syntax.sql b/src/test/regress/sql/mysql_syntax.sql index 4ffeb793b..d965d10cf 100644 --- a/src/test/regress/sql/mysql_syntax.sql +++ b/src/test/regress/sql/mysql_syntax.sql @@ -217,6 +217,30 @@ begin end; / call test_cursor_1(); +create or replace procedure test_cursor_1 +as + company_name varchar(100); + company_loc varchar(100); + company_no integer; + +begin + declare c1_all cursor is --cursor without args + select name, loc, no from company order by 1, 2, 3; + declare c1_all cursor is --cursor without args + select name, loc, no from company order by 1, 2, 3; + if not c1_all%isopen then + open c1_all; + end if; + loop + fetch c1_all into company_name, company_loc, company_no; + exit when c1_all%notfound; + raise notice '% : % : %',company_name,company_loc,company_no; + end loop; + if c1_all%isopen then + close c1_all; + end if; +end; +/ -- test declare condition create or replace procedure test_condition_1 as declare