Commit c1008f00 authored by Heikki Linnakangas's avatar Heikki Linnakangas

Check number of parameters in RAISE statement at compile time.

The number of % parameter markers in RAISE statement should match the number
of parameters given. We used to check that at execution time, but we have
all the information needed at compile time, so let's check it at compile
time instead. It's generally better to find mistakes earlier.

Marko Tiikkaja, reviewed by Fabien Coelho
parent f8f42279
...@@ -3403,6 +3403,9 @@ RAISE ; ...@@ -3403,6 +3403,9 @@ RAISE ;
Inside the format string, <literal>%</literal> is replaced by the Inside the format string, <literal>%</literal> is replaced by the
string representation of the next optional argument's value. Write string representation of the next optional argument's value. Write
<literal>%%</literal> to emit a literal <literal>%</literal>. <literal>%%</literal> to emit a literal <literal>%</literal>.
The number of arguments must match the number of <literal>%</>
placeholders in the format string, or an error is raised during
the compilation of the function.
</para> </para>
<para> <para>
......
...@@ -2939,10 +2939,9 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt) ...@@ -2939,10 +2939,9 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
continue; continue;
} }
/* should have been checked at compile time */
if (current_param == NULL) if (current_param == NULL)
ereport(ERROR, elog(ERROR, "unexpected RAISE parameter list length");
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("too few parameters specified for RAISE")));
paramvalue = exec_eval_expr(estate, paramvalue = exec_eval_expr(estate,
(PLpgSQL_expr *) lfirst(current_param), (PLpgSQL_expr *) lfirst(current_param),
...@@ -2963,14 +2962,9 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt) ...@@ -2963,14 +2962,9 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
appendStringInfoChar(&ds, cp[0]); appendStringInfoChar(&ds, cp[0]);
} }
/* /* should have been checked at compile time */
* If more parameters were specified than were required to process the
* format string, throw an error
*/
if (current_param != NULL) if (current_param != NULL)
ereport(ERROR, elog(ERROR, "unexpected RAISE parameter list length");
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("too many parameters specified for RAISE")));
err_message = ds.data; err_message = ds.data;
/* No pfree(ds.data), the pfree(err_message) does it */ /* No pfree(ds.data), the pfree(err_message) does it */
......
...@@ -106,6 +106,7 @@ static void check_labels(const char *start_label, ...@@ -106,6 +106,7 @@ static void check_labels(const char *start_label,
static PLpgSQL_expr *read_cursor_args(PLpgSQL_var *cursor, static PLpgSQL_expr *read_cursor_args(PLpgSQL_var *cursor,
int until, const char *expected); int until, const char *expected);
static List *read_raise_options(void); static List *read_raise_options(void);
static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
%} %}
...@@ -1849,6 +1850,8 @@ stmt_raise : K_RAISE ...@@ -1849,6 +1850,8 @@ stmt_raise : K_RAISE
new->options = read_raise_options(); new->options = read_raise_options();
} }
check_raise_parameters(new);
$$ = (PLpgSQL_stmt *)new; $$ = (PLpgSQL_stmt *)new;
} }
; ;
...@@ -3767,6 +3770,41 @@ read_raise_options(void) ...@@ -3767,6 +3770,41 @@ read_raise_options(void)
return result; return result;
} }
/*
* Check that the number of parameter placeholders in the message matches the
* number of parameters passed to it, if a message was given.
*/
static void
check_raise_parameters(PLpgSQL_stmt_raise *stmt)
{
char *cp;
int expected_nparams = 0;
if (stmt->message == NULL)
return;
for (cp = stmt->message; *cp; cp++)
{
if (cp[0] == '%')
{
/* ignore literal % characters */
if (cp[1] == '%')
cp++;
else
expected_nparams++;
}
}
if (expected_nparams < list_length(stmt->params))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("too many parameters specified for RAISE")));
if (expected_nparams > list_length(stmt->params))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("too few parameters specified for RAISE")));
}
/* /*
* Fix up CASE statement * Fix up CASE statement
*/ */
......
...@@ -2446,18 +2446,29 @@ begin ...@@ -2446,18 +2446,29 @@ begin
return $1; return $1;
end; end;
$$ language plpgsql; $$ language plpgsql;
select raise_test1(5);
ERROR: too many parameters specified for RAISE ERROR: too many parameters specified for RAISE
CONTEXT: PL/pgSQL function raise_test1(integer) line 3 at RAISE CONTEXT: compilation of PL/pgSQL function "raise_test1" near line 3
create function raise_test2(int) returns int as $$ create function raise_test2(int) returns int as $$
begin begin
raise notice 'This message has too few parameters: %, %, %', $1, $1; raise notice 'This message has too few parameters: %, %, %', $1, $1;
return $1; return $1;
end; end;
$$ language plpgsql; $$ language plpgsql;
select raise_test2(10);
ERROR: too few parameters specified for RAISE ERROR: too few parameters specified for RAISE
CONTEXT: PL/pgSQL function raise_test2(integer) line 3 at RAISE CONTEXT: compilation of PL/pgSQL function "raise_test2" near line 3
create function raise_test3(int) returns int as $$
begin
raise notice 'This message has no parameters (despite having %% signs in it)!';
return $1;
end;
$$ language plpgsql;
select raise_test3(1);
NOTICE: This message has no parameters (despite having % signs in it)!
raise_test3
-------------
1
(1 row)
-- Test re-RAISE inside a nested exception block. This case is allowed -- Test re-RAISE inside a nested exception block. This case is allowed
-- by Oracle's PL/SQL but was handled differently by PG before 9.1. -- by Oracle's PL/SQL but was handled differently by PG before 9.1.
CREATE FUNCTION reraise_test() RETURNS void AS $$ CREATE FUNCTION reraise_test() RETURNS void AS $$
......
...@@ -2078,8 +2078,6 @@ begin ...@@ -2078,8 +2078,6 @@ begin
end; end;
$$ language plpgsql; $$ language plpgsql;
select raise_test1(5);
create function raise_test2(int) returns int as $$ create function raise_test2(int) returns int as $$
begin begin
raise notice 'This message has too few parameters: %, %, %', $1, $1; raise notice 'This message has too few parameters: %, %, %', $1, $1;
...@@ -2087,7 +2085,14 @@ begin ...@@ -2087,7 +2085,14 @@ begin
end; end;
$$ language plpgsql; $$ language plpgsql;
select raise_test2(10); create function raise_test3(int) returns int as $$
begin
raise notice 'This message has no parameters (despite having %% signs in it)!';
return $1;
end;
$$ language plpgsql;
select raise_test3(1);
-- Test re-RAISE inside a nested exception block. This case is allowed -- Test re-RAISE inside a nested exception block. This case is allowed
-- by Oracle's PL/SQL but was handled differently by PG before 9.1. -- by Oracle's PL/SQL but was handled differently by PG before 9.1.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment