* variables (such as FOUND), and is named after the function itself.
*/
plpgsql_ns_init();
- plpgsql_ns_push(NameStr(procStruct->proname));
+ plpgsql_ns_push(NameStr(procStruct->proname), PLPGSQL_LABEL_BLOCK);
plpgsql_DumpExecTree = false;
plpgsql_start_datums();
function->extra_errors = 0;
plpgsql_ns_init();
- plpgsql_ns_push(func_name);
+ plpgsql_ns_push(func_name, PLPGSQL_LABEL_BLOCK);
plpgsql_DumpExecTree = false;
plpgsql_start_datums();
{
estate.err_stmt = NULL;
estate.err_text = NULL;
-
- /*
- * Provide a more helpful message if a CONTINUE has been used outside
- * the context it can work in.
- */
- if (rc == PLPGSQL_RC_CONTINUE)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("CONTINUE cannot be used outside a loop")));
- else
- ereport(ERROR,
- (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
- errmsg("control reached end of function without RETURN")));
+ ereport(ERROR,
+ (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
+ errmsg("control reached end of function without RETURN")));
}
/*
{
estate.err_stmt = NULL;
estate.err_text = NULL;
-
- /*
- * Provide a more helpful message if a CONTINUE has been used outside
- * the context it can work in.
- */
- if (rc == PLPGSQL_RC_CONTINUE)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("CONTINUE cannot be used outside a loop")));
- else
- ereport(ERROR,
- (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
- errmsg("control reached end of trigger procedure without RETURN")));
+ ereport(ERROR,
+ (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
+ errmsg("control reached end of trigger procedure without RETURN")));
}
estate.err_stmt = NULL;
{
estate.err_stmt = NULL;
estate.err_text = NULL;
-
- /*
- * Provide a more helpful message if a CONTINUE has been used outside
- * the context it can work in.
- */
- if (rc == PLPGSQL_RC_CONTINUE)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("CONTINUE cannot be used outside a loop")));
- else
- ereport(ERROR,
- (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
- errmsg("control reached end of trigger procedure without RETURN")));
+ ereport(ERROR,
+ (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
+ errmsg("control reached end of trigger procedure without RETURN")));
}
estate.err_stmt = NULL;
* ----------
*/
void
-plpgsql_ns_push(const char *label)
+plpgsql_ns_push(const char *label, enum PLpgSQL_label_types label_type)
{
if (label == NULL)
label = "";
- plpgsql_ns_additem(PLPGSQL_NSTYPE_LABEL, 0, label);
+ plpgsql_ns_additem(PLPGSQL_NSTYPE_LABEL, (int) label_type, label);
}
}
+/* ----------
+ * plpgsql_ns_find_nearest_loop Find innermost loop label in namespace chain
+ * ----------
+ */
+PLpgSQL_nsitem *
+plpgsql_ns_find_nearest_loop(PLpgSQL_nsitem *ns_cur)
+{
+ while (ns_cur != NULL)
+ {
+ if (ns_cur->itemtype == PLPGSQL_NSTYPE_LABEL &&
+ ns_cur->itemno == PLPGSQL_LABEL_LOOP)
+ return ns_cur;
+ ns_cur = ns_cur->prev;
+ }
+
+ return NULL; /* no loop found */
+}
+
+
/*
* Statement type as a string, for use in error messages etc.
*/
%type <forvariable> for_variable
%type <stmt> for_control
-%type <str> any_identifier opt_block_label opt_label option_value
+%type <str> any_identifier opt_block_label opt_loop_label opt_label
+%type <str> option_value
%type <list> proc_sect stmt_elsifs stmt_else
%type <loop_body> loop_body
$4->itemno, $1.name);
}
| decl_varname opt_scrollable K_CURSOR
- { plpgsql_ns_push($1.name); }
+ { plpgsql_ns_push($1.name, PLPGSQL_LABEL_OTHER); }
decl_cursor_args decl_is_for decl_cursor_query
{
PLpgSQL_var *new;
}
;
-stmt_loop : opt_block_label K_LOOP loop_body
+stmt_loop : opt_loop_label K_LOOP loop_body
{
PLpgSQL_stmt_loop *new;
}
;
-stmt_while : opt_block_label K_WHILE expr_until_loop loop_body
+stmt_while : opt_loop_label K_WHILE expr_until_loop loop_body
{
PLpgSQL_stmt_while *new;
}
;
-stmt_for : opt_block_label K_FOR for_control loop_body
+stmt_for : opt_loop_label K_FOR for_control loop_body
{
/* This runs after we've scanned the loop body */
if ($3->cmd_type == PLPGSQL_STMT_FORI)
}
check_labels($1, $4.end_label, $4.end_label_location);
- /* close namespace started in opt_block_label */
+ /* close namespace started in opt_loop_label */
plpgsql_ns_pop();
}
;
}
;
-stmt_foreach_a : opt_block_label K_FOREACH for_variable foreach_slice K_IN K_ARRAY expr_until_loop loop_body
+stmt_foreach_a : opt_loop_label K_FOREACH for_variable foreach_slice K_IN K_ARRAY expr_until_loop loop_body
{
PLpgSQL_stmt_foreach_a *new;
new->label = $2;
new->cond = $3;
+ if ($2)
+ {
+ /* We have a label, so verify it exists */
+ PLpgSQL_nsitem *label;
+
+ label = plpgsql_ns_lookup_label(plpgsql_ns_top(), $2);
+ if (label == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("label \"%s\" does not exist",
+ $2),
+ parser_errposition(@2)));
+ /* CONTINUE only allows loop labels */
+ if (label->itemno != PLPGSQL_LABEL_LOOP && !$1)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("block label \"%s\" cannot be used in CONTINUE",
+ $2),
+ parser_errposition(@2)));
+ }
+ else
+ {
+ /*
+ * No label, so make sure there is some loop (an
+ * unlabelled EXIT does not match a block, so this
+ * is the same test for both EXIT and CONTINUE)
+ */
+ if (plpgsql_ns_find_nearest_loop(plpgsql_ns_top()) == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ /* translator: %s is EXIT or CONTINUE */
+ errmsg("%s cannot be used outside a loop",
+ plpgsql_stmt_typename((PLpgSQL_stmt *) new)),
+ parser_errposition(@1)));
+ }
+
$$ = (PLpgSQL_stmt *)new;
}
;
opt_block_label :
{
- plpgsql_ns_push(NULL);
+ plpgsql_ns_push(NULL, PLPGSQL_LABEL_BLOCK);
+ $$ = NULL;
+ }
+ | LESS_LESS any_identifier GREATER_GREATER
+ {
+ plpgsql_ns_push($2, PLPGSQL_LABEL_BLOCK);
+ $$ = $2;
+ }
+ ;
+
+opt_loop_label :
+ {
+ plpgsql_ns_push(NULL, PLPGSQL_LABEL_LOOP);
$$ = NULL;
}
| LESS_LESS any_identifier GREATER_GREATER
{
- plpgsql_ns_push($2);
+ plpgsql_ns_push($2, PLPGSQL_LABEL_LOOP);
$$ = $2;
}
;
}
| any_identifier
{
- if (plpgsql_ns_lookup_label(plpgsql_ns_top(), $1) == NULL)
- yyerror("label does not exist");
+ /* label validity will be checked by outer production */
$$ = $1;
}
;
PLPGSQL_NSTYPE_REC
};
+/* ----------
+ * A PLPGSQL_NSTYPE_LABEL stack entry must be one of these types
+ * ----------
+ */
+enum PLpgSQL_label_types
+{
+ PLPGSQL_LABEL_BLOCK, /* DECLARE/BEGIN block */
+ PLPGSQL_LABEL_LOOP, /* looping construct */
+ PLPGSQL_LABEL_OTHER /* anything else */
+};
+
/* ----------
* Datum array node types
* ----------
{ /* Item in the compilers namespace tree */
int itemtype;
int itemno;
+ /* For labels, itemno is a value of enum PLpgSQL_label_types. */
+ /* For other itemtypes, itemno is the associated PLpgSQL_datum's dno. */
struct PLpgSQL_nsitem *prev;
char name[FLEXIBLE_ARRAY_MEMBER]; /* nul-terminated string */
} PLpgSQL_nsitem;
* ----------
*/
extern void plpgsql_ns_init(void);
-extern void plpgsql_ns_push(const char *label);
+extern void plpgsql_ns_push(const char *label,
+ enum PLpgSQL_label_types label_type);
extern void plpgsql_ns_pop(void);
extern PLpgSQL_nsitem *plpgsql_ns_top(void);
extern void plpgsql_ns_additem(int itemtype, int itemno, const char *name);
const char *name3, int *names_used);
extern PLpgSQL_nsitem *plpgsql_ns_lookup_label(PLpgSQL_nsitem *ns_cur,
const char *name);
+extern PLpgSQL_nsitem *plpgsql_ns_find_nearest_loop(PLpgSQL_nsitem *ns_cur);
/* ----------
* Other functions in pl_funcs.c
(1 row)
--- CONTINUE is only legal inside a loop
-create function continue_test2() returns void as $$
+drop function continue_test1();
+drop table conttesttbl;
+-- should fail: CONTINUE is only legal inside a loop
+create function continue_error1() returns void as $$
begin
begin
continue;
end;
- return;
end;
$$ language plpgsql;
--- should fail
-select continue_test2();
ERROR: CONTINUE cannot be used outside a loop
-CONTEXT: PL/pgSQL function continue_test2()
--- CONTINUE can't reference the label of a named block
-create function continue_test3() returns void as $$
+LINE 4: continue;
+ ^
+-- should fail: EXIT is only legal inside a loop
+create function exit_error1() returns void as $$
+begin
+ begin
+ exit;
+ end;
+end;
+$$ language plpgsql;
+ERROR: EXIT cannot be used outside a loop
+LINE 4: exit;
+ ^
+-- should fail: no such label
+create function continue_error2() returns void as $$
+begin
+ begin
+ loop
+ continue no_such_label;
+ end loop;
+ end;
+end;
+$$ language plpgsql;
+ERROR: label "no_such_label" does not exist
+LINE 5: continue no_such_label;
+ ^
+-- should fail: no such label
+create function exit_error2() returns void as $$
+begin
+ begin
+ loop
+ exit no_such_label;
+ end loop;
+ end;
+end;
+$$ language plpgsql;
+ERROR: label "no_such_label" does not exist
+LINE 5: exit no_such_label;
+ ^
+-- should fail: CONTINUE can't reference the label of a named block
+create function continue_error3() returns void as $$
begin
<<begin_block1>>
begin
end;
end;
$$ language plpgsql;
--- should fail
-select continue_test3();
-ERROR: CONTINUE cannot be used outside a loop
-CONTEXT: PL/pgSQL function continue_test3()
-drop function continue_test1();
-drop function continue_test2();
-drop function continue_test3();
-drop table conttesttbl;
+ERROR: block label "begin_block1" cannot be used in CONTINUE
+LINE 6: continue begin_block1;
+ ^
+-- On the other hand, EXIT *can* reference the label of a named block
+create function exit_block1() returns void as $$
+begin
+ <<begin_block1>>
+ begin
+ loop
+ exit begin_block1;
+ raise exception 'should not get here';
+ end loop;
+ end;
+end;
+$$ language plpgsql;
+select exit_block1();
+ exit_block1
+-------------
+
+(1 row)
+
+drop function exit_block1();
-- verbose end block and end loop
create function end_label1() returns void as $$
<<blbl>>
end loop flbl1;
end;
$$ language plpgsql;
-ERROR: label does not exist at or near "flbl1"
+ERROR: end label "flbl1" specified for unlabelled block
LINE 5: end loop flbl1;
^
-- should fail: end label does not match start label
select continue_test1();
--- CONTINUE is only legal inside a loop
-create function continue_test2() returns void as $$
+drop function continue_test1();
+drop table conttesttbl;
+
+-- should fail: CONTINUE is only legal inside a loop
+create function continue_error1() returns void as $$
begin
begin
continue;
end;
- return;
end;
$$ language plpgsql;
--- should fail
-select continue_test2();
+-- should fail: EXIT is only legal inside a loop
+create function exit_error1() returns void as $$
+begin
+ begin
+ exit;
+ end;
+end;
+$$ language plpgsql;
+
+-- should fail: no such label
+create function continue_error2() returns void as $$
+begin
+ begin
+ loop
+ continue no_such_label;
+ end loop;
+ end;
+end;
+$$ language plpgsql;
+
+-- should fail: no such label
+create function exit_error2() returns void as $$
+begin
+ begin
+ loop
+ exit no_such_label;
+ end loop;
+ end;
+end;
+$$ language plpgsql;
--- CONTINUE can't reference the label of a named block
-create function continue_test3() returns void as $$
+-- should fail: CONTINUE can't reference the label of a named block
+create function continue_error3() returns void as $$
begin
<<begin_block1>>
begin
end;
$$ language plpgsql;
--- should fail
-select continue_test3();
+-- On the other hand, EXIT *can* reference the label of a named block
+create function exit_block1() returns void as $$
+begin
+ <<begin_block1>>
+ begin
+ loop
+ exit begin_block1;
+ raise exception 'should not get here';
+ end loop;
+ end;
+end;
+$$ language plpgsql;
-drop function continue_test1();
-drop function continue_test2();
-drop function continue_test3();
-drop table conttesttbl;
+select exit_block1();
+drop function exit_block1();
-- verbose end block and end loop
create function end_label1() returns void as $$