diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml index 91fadb0f14d..f008e937eef 100644 --- a/doc/src/sgml/plpgsql.sgml +++ b/doc/src/sgml/plpgsql.sgml @@ -3403,6 +3403,9 @@ RAISE ; Inside the format string, % is replaced by the string representation of the next optional argument's value. Write %% to emit a literal %. + The number of arguments must match the number of % + placeholders in the format string, or an error is raised during + the compilation of the function. diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 69d1965253d..11cb47b5225 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -2939,10 +2939,9 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt) continue; } + /* should have been checked at compile time */ if (current_param == NULL) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("too few parameters specified for RAISE"))); + elog(ERROR, "unexpected RAISE parameter list length"); paramvalue = exec_eval_expr(estate, (PLpgSQL_expr *) lfirst(current_param), @@ -2963,14 +2962,9 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt) appendStringInfoChar(&ds, cp[0]); } - /* - * If more parameters were specified than were required to process the - * format string, throw an error - */ + /* should have been checked at compile time */ if (current_param != NULL) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("too many parameters specified for RAISE"))); + elog(ERROR, "unexpected RAISE parameter list length"); err_message = ds.data; /* No pfree(ds.data), the pfree(err_message) does it */ diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y index e3a992cf0ff..893f3a486f5 100644 --- a/src/pl/plpgsql/src/pl_gram.y +++ b/src/pl/plpgsql/src/pl_gram.y @@ -106,6 +106,7 @@ static void check_labels(const char *start_label, static PLpgSQL_expr *read_cursor_args(PLpgSQL_var *cursor, int until, const char *expected); static List *read_raise_options(void); +static void check_raise_parameters(PLpgSQL_stmt_raise *stmt); %} @@ -1849,6 +1850,8 @@ stmt_raise : K_RAISE new->options = read_raise_options(); } + check_raise_parameters(new); + $$ = (PLpgSQL_stmt *)new; } ; @@ -3767,6 +3770,41 @@ read_raise_options(void) 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 */ diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out index 8892bb417d3..983f1b8b5a4 100644 --- a/src/test/regress/expected/plpgsql.out +++ b/src/test/regress/expected/plpgsql.out @@ -2446,18 +2446,29 @@ begin return $1; end; $$ language plpgsql; -select raise_test1(5); 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 $$ begin raise notice 'This message has too few parameters: %, %, %', $1, $1; return $1; end; $$ language plpgsql; -select raise_test2(10); 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 -- by Oracle's PL/SQL but was handled differently by PG before 9.1. CREATE FUNCTION reraise_test() RETURNS void AS $$ diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql index c5616ee8fc6..2abcbc8d3cd 100644 --- a/src/test/regress/sql/plpgsql.sql +++ b/src/test/regress/sql/plpgsql.sql @@ -2078,8 +2078,6 @@ begin end; $$ language plpgsql; -select raise_test1(5); - create function raise_test2(int) returns int as $$ begin raise notice 'This message has too few parameters: %, %, %', $1, $1; @@ -2087,7 +2085,14 @@ begin end; $$ 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 -- by Oracle's PL/SQL but was handled differently by PG before 9.1.