diff --git a/src/common/pl/plpgsql/src/gram.y b/src/common/pl/plpgsql/src/gram.y index 35437a5d9..2aa7e4487 100755 --- a/src/common/pl/plpgsql/src/gram.y +++ b/src/common/pl/plpgsql/src/gram.y @@ -479,7 +479,9 @@ static bool need_build_row_for_func_arg(PLpgSQL_rec **rec, PLpgSQL_row **row, in %token K_INTERSECT %token K_INTO %token K_IS +%token K_ITERATE %token K_LAST +%token K_LEAVE %token K_LIMIT %token K_LOG %token K_LOOP @@ -2731,6 +2733,32 @@ stmt_loop : opt_block_label K_LOOP loop_body /* register the stmt if it is labeled */ record_stmt_label($1, (PLpgSQL_stmt *)newp); } + | ':' K_LOOP loop_body + { + /* + * When the database is in mysql compatible mode + * support "label: loop" + */ + if(u_sess->attr.attr_sql.sql_compatibility != B_FORMAT) + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("'label:' is only supported in database which dbcompatibility='B'."))); + + PLpgSQL_stmt_loop *newp; + newp = (PLpgSQL_stmt_loop *)palloc0(sizeof(PLpgSQL_stmt_loop)); + newp->cmd_type = PLPGSQL_STMT_LOOP; + newp->lineno = plpgsql_location_to_lineno(@2); + newp->label = u_sess->plsql_cxt.curr_compile_context->ns_top->name; + newp->body = $3.stmts; + newp->sqlString = plpgsql_get_curline_query(); + + check_labels(u_sess->plsql_cxt.curr_compile_context->ns_top->name, $3.end_label, $3.end_label_location); + plpgsql_ns_pop(); + + $$ = (PLpgSQL_stmt *)newp; + + /* register the stmt if it is labeled */ + record_stmt_label(u_sess->plsql_cxt.curr_compile_context->ns_top->name, (PLpgSQL_stmt *)newp); + } ; stmt_while : opt_block_label K_WHILE expr_until_loop loop_body @@ -3530,6 +3558,30 @@ exit_type : K_EXIT } | K_CONTINUE { + $$ = false; + } + | K_LEAVE + { + /* + * When the database is in mysql compatible mode + * support "leave label" + */ + if(u_sess->attr.attr_sql.sql_compatibility != B_FORMAT) + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("'LEAVE label' is only in database which dbcompatibility='B'."))); + + $$ = true; + } + | K_ITERATE + { + /* + * When the database is in mysql compatible mode + * support "iterate label" + */ + if(u_sess->attr.attr_sql.sql_compatibility != B_FORMAT) + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("'ITERATE label' is only in database which dbcompatibility='B'."))); + $$ = false; } ; @@ -8473,7 +8525,7 @@ make_execsql_stmt(int firsttoken, int location) int parenlevel = 0; List *list_bracket = 0; /* stack structure bracket tracker */ List *list_bracket_loc = 0; /* location tracker */ - + bool label_loop = false; initStringInfo(&ds); /* special lookup mode for identifiers within the SQL text */ @@ -8586,6 +8638,55 @@ make_execsql_stmt(int firsttoken, int location) u_sess->plsql_cxt.curr_compile_context->plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_EXPR; } + /* + * When the database is in mysql compatible mode, + * the loop syntax of mysql is compatible (label: loop) + */ + if (tok == ':' && prev_tok == T_WORD) + { + StringInfoData lb; + initStringInfo(&lb); + int lb_end = yylloc; + int tok1 = yylex(); + if(tok1 == K_LOOP) + { + if(u_sess->attr.attr_sql.sql_compatibility == B_FORMAT) + { + int count = 0; + label_loop = true; + plpgsql_push_back_token(tok1); + plpgsql_push_back_token(tok); + plpgsql_append_source_text(&lb, location, lb_end); + + for(int i = lb.len-1; i > 0; i--) + { + if(lb.data[i] == ' ') + { + count++; + } + else + break; + } + if(count > 0) + { + char* name = (char*)palloc(lb.len-count+1); + strncpy(name, lb.data, lb.len-count); + name[lb.len-count] = '\0'; + plpgsql_ns_additem(PLPGSQL_NSTYPE_LABEL, 0, pg_strtolower(name)); + pfree(name); + } + else + plpgsql_ns_additem(PLPGSQL_NSTYPE_LABEL, 0, pg_strtolower(lb.data)); + + pfree_ext(lb.data); + break; + } + else + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("'label:' is only supported in database which dbcompatibility='B'."))); + } + } + if (tok == T_CWORD && prev_tok!=K_SELECT && prev_tok!= K_PERFORM) { List *dtname = yylval.cword.idents; @@ -8870,6 +8971,9 @@ make_execsql_stmt(int firsttoken, int location) } } } + } else if (label_loop) + { + appendStringInfoString(&ds, "\n"); } else { plpgsql_append_source_text(&ds, location, yylloc); diff --git a/src/common/pl/plpgsql/src/pl_scanner.cpp b/src/common/pl/plpgsql/src/pl_scanner.cpp index 8e0da2bab..d9dd38394 100644 --- a/src/common/pl/plpgsql/src/pl_scanner.cpp +++ b/src/common/pl/plpgsql/src/pl_scanner.cpp @@ -142,8 +142,10 @@ static const ScanKeyword unreserved_keywords[] = { PG_KEYWORD("info", K_INFO, UNRESERVED_KEYWORD) PG_KEYWORD("instantiation", K_INSTANTIATION, UNRESERVED_KEYWORD) PG_KEYWORD("intersect", K_INTERSECT, UNRESERVED_KEYWORD) - PG_KEYWORD("is", K_IS, UNRESERVED_KEYWORD) + PG_KEYWORD("is", K_IS, UNRESERVED_KEYWORD) + PG_KEYWORD("iterate", K_ITERATE, UNRESERVED_KEYWORD) PG_KEYWORD("last", K_LAST, UNRESERVED_KEYWORD) + PG_KEYWORD("leave", K_LEAVE, UNRESERVED_KEYWORD) PG_KEYWORD("log", K_LOG, UNRESERVED_KEYWORD) PG_KEYWORD("merge", K_MERGE, UNRESERVED_KEYWORD) PG_KEYWORD("message", K_MESSAGE, UNRESERVED_KEYWORD) diff --git a/src/test/regress/expected/b_compatibility.out b/src/test/regress/expected/b_compatibility.out index d206a918f..5268e18d1 100644 --- a/src/test/regress/expected/b_compatibility.out +++ b/src/test/regress/expected/b_compatibility.out @@ -916,6 +916,273 @@ SELECT * FROM view_schema.new_view3; RESET ROLE; \c regression +-- test label:loop +--error +create or replace procedure doiterate(p1 int) +as +begin +label1: loop +p1 := p1+1; +if p1 < 10 then +iterate label1; +end if; +leave label1; +end loop label1; +raise notice 'p1:%',p1; +end; +/ +ERROR: 'label:' is only supported in database which dbcompatibility='B'. +CONTEXT: compilation of PL/pgSQL function "doiterate" near line 1 +--error +create or replace procedure doiterate(p1 int) +as +begin +<> loop +p1 := p1+1; +if p1 < 10 then +iterate label1; +end if; +leave label1; +end loop label1; +raise notice 'p1:%',p1; +end; +/ +ERROR: 'ITERATE label' is only in database which dbcompatibility='B'. +CONTEXT: compilation of PL/pgSQL function "doiterate" near line 4 +--error +create or replace procedure doiterate(p1 int) +as +begin +<> loop +p1 := p1+1; +if p1 < 10 then +continue label1; +end if; +leave label1; +end loop label1; +raise notice 'p1:%',p1; +end; +/ +ERROR: 'LEAVE label' is only in database which dbcompatibility='B'. +CONTEXT: compilation of PL/pgSQL function "doiterate" near line 5 +--success +create or replace procedure doiterate(p1 int) +as +begin +<> loop +p1 := p1+1; +if p1 < 10 then +continue label1; +end if; +exit label1; +end loop label1; +raise notice 'p1:%',p1; +end; +/ +call doiterate(3); +NOTICE: p1:10 + doiterate +----------- + +(1 row) + +drop procedure if exists doiterate; +\c b +--success +create or replace procedure doiterate(p1 int) +as +begin +label1 : +loop +p1 := p1+1; +if p1 < 10 then +raise notice '123'; +end if; +exit; +end loop label1; +end; +/ +call doiterate(2); +NOTICE: 123 + doiterate +----------- + +(1 row) + +--success +create or replace procedure doiterate(p1 int) +as +begin +LABEL1: +loop +p1 := p1+1; +if p1 < 10 then +raise notice '123'; +end if; +exit LABEL1; +end loop LABEL1; +end; +/ +call doiterate(2); +NOTICE: 123 + doiterate +----------- + +(1 row) + +--success +create or replace procedure doiterate(p1 int) +as +begin +LAbEL1: +loop +p1 := p1+1; +if p1 < 10 then +raise notice '123'; +end if; +exit LABeL1; +end loop LaBEL1; +end; +/ +call doiterate(2); +NOTICE: 123 + doiterate +----------- + +(1 row) + +--success +create or replace procedure doiterate(p1 int) +as +begin +label1: loop +p1 := p1+1; +if p1 < 10 then +iterate label1; +end if; +leave label1; +end loop label1; +raise notice 'p1:%',p1; +end; +/ +call doiterate(3); +NOTICE: p1:10 + doiterate +----------- + +(1 row) + +create or replace procedure doiterate(p1 int) +as +begin +loop +p1 := p1+1; +if p1 < 10 then +iterate; +end if; +leave; +end loop; +raise notice 'p1:%',p1; +end; +/ +call doiterate(3); +NOTICE: p1:10 + doiterate +----------- + +(1 row) + +create or replace function labeltest(n int) return int +as +p int; +i int; +begin +p :=2; +label1: loop +if p < n then +p := p+1; +iterate label1; +end if; +leave label1; +end loop label1; +return p; +end; +/ +call labeltest(5); + labeltest +----------- + 5 +(1 row) + +create or replace function labeltest(n int) return int +as +p int; +i int; +begin +p :=2; +LABEL1: loop +if p < n then +p := p+1; +iterate LABEL1; +end if; +leave LABEL1; +end loop LABEL1; +return p; +end; +/ +call labeltest(3); + labeltest +----------- + 3 +(1 row) + +create or replace function labeltest(n int) return int +as +p int; +i int; +begin +p :=2; +lAbel1: loop +if p < n then +p := p+1; +iterate labEl1; +end if; +leave LAbel1; +end loop labEL1; +return p; +end; +/ +call labeltest(7); + labeltest +----------- + 7 +(1 row) + +create or replace function labeltest(n int) return int +as +p int; +i int; +begin +p :=2; +loop +if p < n then +p := p+1; +iterate; +end if; +leave; +end loop; +return p; +end; +/ +call labeltest(9); + labeltest +----------- + 9 +(1 row) + +drop procedure if exists doiterate; +drop function if exists labeltest; +\c regression drop database b; DROP USER test_c; DROP USER test_d; diff --git a/src/test/regress/sql/b_compatibility.sql b/src/test/regress/sql/b_compatibility.sql index 38ca01953..ec3c45346 100644 --- a/src/test/regress/sql/b_compatibility.sql +++ b/src/test/regress/sql/b_compatibility.sql @@ -289,6 +289,235 @@ RESET ROLE; \c regression +-- test label:loop +--error +create or replace procedure doiterate(p1 int) +as +begin +label1: loop +p1 := p1+1; +if p1 < 10 then +iterate label1; +end if; +leave label1; +end loop label1; +raise notice 'p1:%',p1; +end; +/ + +--error +create or replace procedure doiterate(p1 int) +as +begin +<> loop +p1 := p1+1; +if p1 < 10 then +iterate label1; +end if; +leave label1; +end loop label1; +raise notice 'p1:%',p1; +end; +/ + +--error +create or replace procedure doiterate(p1 int) +as +begin +<> loop +p1 := p1+1; +if p1 < 10 then +continue label1; +end if; +leave label1; +end loop label1; +raise notice 'p1:%',p1; +end; +/ + +--success +create or replace procedure doiterate(p1 int) +as +begin +<> loop +p1 := p1+1; +if p1 < 10 then +continue label1; +end if; +exit label1; +end loop label1; +raise notice 'p1:%',p1; +end; +/ + +call doiterate(3); + +drop procedure if exists doiterate; +\c b +--success +create or replace procedure doiterate(p1 int) +as +begin +label1 : +loop +p1 := p1+1; +if p1 < 10 then +raise notice '123'; +end if; +exit; +end loop label1; +end; +/ + +call doiterate(2); + +--success +create or replace procedure doiterate(p1 int) +as +begin +LABEL1: +loop +p1 := p1+1; +if p1 < 10 then +raise notice '123'; +end if; +exit LABEL1; +end loop LABEL1; +end; +/ + +call doiterate(2); + +--success +create or replace procedure doiterate(p1 int) +as +begin +LAbEL1: +loop +p1 := p1+1; +if p1 < 10 then +raise notice '123'; +end if; +exit LABeL1; +end loop LaBEL1; +end; +/ + +call doiterate(2); + +--success +create or replace procedure doiterate(p1 int) +as +begin +label1: loop +p1 := p1+1; +if p1 < 10 then +iterate label1; +end if; +leave label1; +end loop label1; +raise notice 'p1:%',p1; +end; +/ + +call doiterate(3); + +create or replace procedure doiterate(p1 int) +as +begin +loop +p1 := p1+1; +if p1 < 10 then +iterate; +end if; +leave; +end loop; +raise notice 'p1:%',p1; +end; +/ + +call doiterate(3); + +create or replace function labeltest(n int) return int +as +p int; +i int; +begin +p :=2; +label1: loop +if p < n then +p := p+1; +iterate label1; +end if; +leave label1; +end loop label1; +return p; +end; +/ + +call labeltest(5); + +create or replace function labeltest(n int) return int +as +p int; +i int; +begin +p :=2; +LABEL1: loop +if p < n then +p := p+1; +iterate LABEL1; +end if; +leave LABEL1; +end loop LABEL1; +return p; +end; +/ + +call labeltest(3); + +create or replace function labeltest(n int) return int +as +p int; +i int; +begin +p :=2; +lAbel1: loop +if p < n then +p := p+1; +iterate labEl1; +end if; +leave LAbel1; +end loop labEL1; +return p; +end; +/ + +call labeltest(7); + +create or replace function labeltest(n int) return int +as +p int; +i int; +begin +p :=2; +loop +if p < n then +p := p+1; +iterate; +end if; +leave; +end loop; +return p; +end; +/ + +call labeltest(9); +drop procedure if exists doiterate; +drop function if exists labeltest; + +\c regression + drop database b; DROP USER test_c; DROP USER test_d;