Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitfcdfce6

Browse files
committed
Detect mismatched CONTINUE and EXIT statements at plpgsql compile time.
With a bit of tweaking of the compile namestack data structure, we canverify at compile time whether a CONTINUE or EXIT is legal. This issurely better than leaving it to runtime, both because earlier is betterand because we can issue a proper error pointer. Also, we can get ridof the ad-hoc old way of detecting the problem, which only took care ofCONTINUE not EXIT.Jim Nasby, adjusted a bit by me
1 parent072710d commitfcdfce6

File tree

7 files changed

+226
-85
lines changed

7 files changed

+226
-85
lines changed

‎src/pl/plpgsql/src/pl_comp.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ do_compile(FunctionCallInfo fcinfo,
371371
* variables (such as FOUND), and is named after the function itself.
372372
*/
373373
plpgsql_ns_init();
374-
plpgsql_ns_push(NameStr(procStruct->proname));
374+
plpgsql_ns_push(NameStr(procStruct->proname),PLPGSQL_LABEL_BLOCK);
375375
plpgsql_DumpExecTree= false;
376376
plpgsql_start_datums();
377377

@@ -851,7 +851,7 @@ plpgsql_compile_inline(char *proc_source)
851851
function->extra_errors=0;
852852

853853
plpgsql_ns_init();
854-
plpgsql_ns_push(func_name);
854+
plpgsql_ns_push(func_name,PLPGSQL_LABEL_BLOCK);
855855
plpgsql_DumpExecTree= false;
856856
plpgsql_start_datums();
857857

‎src/pl/plpgsql/src/pl_exec.c

Lines changed: 9 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -437,19 +437,9 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo,
437437
{
438438
estate.err_stmt=NULL;
439439
estate.err_text=NULL;
440-
441-
/*
442-
* Provide a more helpful message if a CONTINUE has been used outside
443-
* the context it can work in.
444-
*/
445-
if (rc==PLPGSQL_RC_CONTINUE)
446-
ereport(ERROR,
447-
(errcode(ERRCODE_SYNTAX_ERROR),
448-
errmsg("CONTINUE cannot be used outside a loop")));
449-
else
450-
ereport(ERROR,
451-
(errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
452-
errmsg("control reached end of function without RETURN")));
440+
ereport(ERROR,
441+
(errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
442+
errmsg("control reached end of function without RETURN")));
453443
}
454444

455445
/*
@@ -791,19 +781,9 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
791781
{
792782
estate.err_stmt=NULL;
793783
estate.err_text=NULL;
794-
795-
/*
796-
* Provide a more helpful message if a CONTINUE has been used outside
797-
* the context it can work in.
798-
*/
799-
if (rc==PLPGSQL_RC_CONTINUE)
800-
ereport(ERROR,
801-
(errcode(ERRCODE_SYNTAX_ERROR),
802-
errmsg("CONTINUE cannot be used outside a loop")));
803-
else
804-
ereport(ERROR,
805-
(errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
806-
errmsg("control reached end of trigger procedure without RETURN")));
784+
ereport(ERROR,
785+
(errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
786+
errmsg("control reached end of trigger procedure without RETURN")));
807787
}
808788

809789
estate.err_stmt=NULL;
@@ -919,19 +899,9 @@ plpgsql_exec_event_trigger(PLpgSQL_function *func, EventTriggerData *trigdata)
919899
{
920900
estate.err_stmt=NULL;
921901
estate.err_text=NULL;
922-
923-
/*
924-
* Provide a more helpful message if a CONTINUE has been used outside
925-
* the context it can work in.
926-
*/
927-
if (rc==PLPGSQL_RC_CONTINUE)
928-
ereport(ERROR,
929-
(errcode(ERRCODE_SYNTAX_ERROR),
930-
errmsg("CONTINUE cannot be used outside a loop")));
931-
else
932-
ereport(ERROR,
933-
(errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
934-
errmsg("control reached end of trigger procedure without RETURN")));
902+
ereport(ERROR,
903+
(errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
904+
errmsg("control reached end of trigger procedure without RETURN")));
935905
}
936906

937907
estate.err_stmt=NULL;

‎src/pl/plpgsql/src/pl_funcs.c

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,11 @@ plpgsql_ns_init(void)
5151
* ----------
5252
*/
5353
void
54-
plpgsql_ns_push(constchar*label)
54+
plpgsql_ns_push(constchar*label,enumPLpgSQL_label_typeslabel_type)
5555
{
5656
if (label==NULL)
5757
label="";
58-
plpgsql_ns_additem(PLPGSQL_NSTYPE_LABEL,0,label);
58+
plpgsql_ns_additem(PLPGSQL_NSTYPE_LABEL,(int)label_type,label);
5959
}
6060

6161

@@ -206,6 +206,25 @@ plpgsql_ns_lookup_label(PLpgSQL_nsitem *ns_cur, const char *name)
206206
}
207207

208208

209+
/* ----------
210+
* plpgsql_ns_find_nearest_loopFind innermost loop label in namespace chain
211+
* ----------
212+
*/
213+
PLpgSQL_nsitem*
214+
plpgsql_ns_find_nearest_loop(PLpgSQL_nsitem*ns_cur)
215+
{
216+
while (ns_cur!=NULL)
217+
{
218+
if (ns_cur->itemtype==PLPGSQL_NSTYPE_LABEL&&
219+
ns_cur->itemno==PLPGSQL_LABEL_LOOP)
220+
returnns_cur;
221+
ns_cur=ns_cur->prev;
222+
}
223+
224+
returnNULL;/* no loop found */
225+
}
226+
227+
209228
/*
210229
* Statement type as a string, for use in error messages etc.
211230
*/

‎src/pl/plpgsql/src/pl_gram.y

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,8 @@ staticvoidcheck_raise_parameters(PLpgSQL_stmt_raise *stmt);
186186
%type<forvariable>for_variable
187187
%type<stmt>for_control
188188

189-
%type<str>any_identifieropt_block_labelopt_labeloption_value
189+
%type<str>any_identifieropt_block_labelopt_loop_labelopt_label
190+
%type<str>option_value
190191

191192
%type<list>proc_sectstmt_elsifsstmt_else
192193
%type<loop_body>loop_body
@@ -535,7 +536,7 @@ decl_statement: decl_varname decl_const decl_datatype decl_collate decl_notnull
535536
$4->itemno, $1.name);
536537
}
537538
| decl_varname opt_scrollable K_CURSOR
538-
{ plpgsql_ns_push($1.name); }
539+
{ plpgsql_ns_push($1.name, PLPGSQL_LABEL_OTHER); }
539540
decl_cursor_args decl_is_for decl_cursor_query
540541
{
541542
PLpgSQL_var *new;
@@ -1218,7 +1219,7 @@ opt_case_else:
12181219
}
12191220
;
12201221

1221-
stmt_loop:opt_block_label K_LOOP loop_body
1222+
stmt_loop:opt_loop_label K_LOOP loop_body
12221223
{
12231224
PLpgSQL_stmt_loop *new;
12241225

@@ -1235,7 +1236,7 @@ stmt_loop: opt_block_label K_LOOP loop_body
12351236
}
12361237
;
12371238

1238-
stmt_while:opt_block_label K_WHILE expr_until_loop loop_body
1239+
stmt_while:opt_loop_label K_WHILE expr_until_loop loop_body
12391240
{
12401241
PLpgSQL_stmt_while *new;
12411242

@@ -1253,7 +1254,7 @@ stmt_while: opt_block_label K_WHILE expr_until_loop loop_body
12531254
}
12541255
;
12551256

1256-
stmt_for:opt_block_label K_FOR for_control loop_body
1257+
stmt_for:opt_loop_label K_FOR for_control loop_body
12571258
{
12581259
/* This runs after we've scanned the loop body*/
12591260
if ($3->cmd_type == PLPGSQL_STMT_FORI)
@@ -1282,7 +1283,7 @@ stmt_for: opt_block_label K_FOR for_control loop_body
12821283
}
12831284

12841285
check_labels($1, $4.end_label, $4.end_label_location);
1285-
/* close namespace started inopt_block_label*/
1286+
/* close namespace started inopt_loop_label*/
12861287
plpgsql_ns_pop();
12871288
}
12881289
;
@@ -1603,7 +1604,7 @@ for_variable: T_DATUM
16031604
}
16041605
;
16051606

1606-
stmt_foreach_a:opt_block_label K_FOREACH for_variable foreach_slice K_IN K_ARRAY expr_until_loop loop_body
1607+
stmt_foreach_a:opt_loop_label K_FOREACH for_variable foreach_slice K_IN K_ARRAY expr_until_loop loop_body
16071608
{
16081609
PLpgSQL_stmt_foreach_a *new;
16091610

@@ -1666,6 +1667,42 @@ stmt_exit: exit_type opt_label opt_exitcond
16661667
new->label = $2;
16671668
new->cond = $3;
16681669

1670+
if ($2)
1671+
{
1672+
/* We have a label, so verify it exists*/
1673+
PLpgSQL_nsitem *label;
1674+
1675+
label =plpgsql_ns_lookup_label(plpgsql_ns_top(), $2);
1676+
if (label ==NULL)
1677+
ereport(ERROR,
1678+
(errcode(ERRCODE_SYNTAX_ERROR),
1679+
errmsg("label\"%s\" does not exist",
1680+
$2),
1681+
parser_errposition(@2)));
1682+
/* CONTINUE only allows loop labels*/
1683+
if (label->itemno != PLPGSQL_LABEL_LOOP && !$1)
1684+
ereport(ERROR,
1685+
(errcode(ERRCODE_SYNTAX_ERROR),
1686+
errmsg("block label\"%s\" cannot be used in CONTINUE",
1687+
$2),
1688+
parser_errposition(@2)));
1689+
}
1690+
else
1691+
{
1692+
/*
1693+
* No label, so make sure there is some loop (an
1694+
* unlabelled EXIT does not match a block, so this
1695+
* is the same test for both EXIT and CONTINUE)
1696+
*/
1697+
if (plpgsql_ns_find_nearest_loop(plpgsql_ns_top()) ==NULL)
1698+
ereport(ERROR,
1699+
(errcode(ERRCODE_SYNTAX_ERROR),
1700+
/* translator: %s is EXIT or CONTINUE*/
1701+
errmsg("%s cannot be used outside a loop",
1702+
plpgsql_stmt_typename((PLpgSQL_stmt *)new)),
1703+
parser_errposition(@1)));
1704+
}
1705+
16691706
$$ = (PLpgSQL_stmt *)new;
16701707
}
16711708
;
@@ -2290,12 +2327,24 @@ expr_until_loop :
22902327

22912328
opt_block_label:
22922329
{
2293-
plpgsql_ns_push(NULL);
2330+
plpgsql_ns_push(NULL, PLPGSQL_LABEL_BLOCK);
2331+
$$ =NULL;
2332+
}
2333+
| LESS_LESS any_identifier GREATER_GREATER
2334+
{
2335+
plpgsql_ns_push($2, PLPGSQL_LABEL_BLOCK);
2336+
$$ = $2;
2337+
}
2338+
;
2339+
2340+
opt_loop_label:
2341+
{
2342+
plpgsql_ns_push(NULL, PLPGSQL_LABEL_LOOP);
22942343
$$ =NULL;
22952344
}
22962345
| LESS_LESS any_identifier GREATER_GREATER
22972346
{
2298-
plpgsql_ns_push($2);
2347+
plpgsql_ns_push($2, PLPGSQL_LABEL_LOOP);
22992348
$$ = $2;
23002349
}
23012350
;
@@ -2306,8 +2355,7 @@ opt_label:
23062355
}
23072356
| any_identifier
23082357
{
2309-
if (plpgsql_ns_lookup_label(plpgsql_ns_top(), $1) ==NULL)
2310-
yyerror("label does not exist");
2358+
/* label validity will be checked by outer production*/
23112359
$$ = $1;
23122360
}
23132361
;

‎src/pl/plpgsql/src/plpgsql.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,17 @@ enum
4646
PLPGSQL_NSTYPE_REC
4747
};
4848

49+
/* ----------
50+
* A PLPGSQL_NSTYPE_LABEL stack entry must be one of these types
51+
* ----------
52+
*/
53+
enumPLpgSQL_label_types
54+
{
55+
PLPGSQL_LABEL_BLOCK,/* DECLARE/BEGIN block */
56+
PLPGSQL_LABEL_LOOP,/* looping construct */
57+
PLPGSQL_LABEL_OTHER/* anything else */
58+
};
59+
4960
/* ----------
5061
* Datum array node types
5162
* ----------
@@ -331,6 +342,8 @@ typedef struct PLpgSQL_nsitem
331342
{/* Item in the compilers namespace tree */
332343
intitemtype;
333344
intitemno;
345+
/* For labels, itemno is a value of enum PLpgSQL_label_types. */
346+
/* For other itemtypes, itemno is the associated PLpgSQL_datum's dno. */
334347
structPLpgSQL_nsitem*prev;
335348
charname[FLEXIBLE_ARRAY_MEMBER];/* nul-terminated string */
336349
}PLpgSQL_nsitem;
@@ -997,7 +1010,8 @@ extern void exec_get_datum_type_info(PLpgSQL_execstate *estate,
9971010
* ----------
9981011
*/
9991012
externvoidplpgsql_ns_init(void);
1000-
externvoidplpgsql_ns_push(constchar*label);
1013+
externvoidplpgsql_ns_push(constchar*label,
1014+
enumPLpgSQL_label_typeslabel_type);
10011015
externvoidplpgsql_ns_pop(void);
10021016
externPLpgSQL_nsitem*plpgsql_ns_top(void);
10031017
externvoidplpgsql_ns_additem(intitemtype,intitemno,constchar*name);
@@ -1006,6 +1020,7 @@ extern PLpgSQL_nsitem *plpgsql_ns_lookup(PLpgSQL_nsitem *ns_cur, bool localmode,
10061020
constchar*name3,int*names_used);
10071021
externPLpgSQL_nsitem*plpgsql_ns_lookup_label(PLpgSQL_nsitem*ns_cur,
10081022
constchar*name);
1023+
externPLpgSQL_nsitem*plpgsql_ns_find_nearest_loop(PLpgSQL_nsitem*ns_cur);
10091024

10101025
/* ----------
10111026
* Other functions in pl_funcs.c

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp