From c1008f0037ec9c738127c2fa6d7f6c88d885f45f Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Tue, 2 Sep 2014 15:53:06 +0300 Subject: [PATCH] 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 --- doc/src/sgml/plpgsql.sgml | 3 +++ src/pl/plpgsql/src/pl_exec.c | 14 +++------- src/pl/plpgsql/src/pl_gram.y | 38 +++++++++++++++++++++++++++ src/test/regress/expected/plpgsql.out | 19 +++++++++++--- src/test/regress/sql/plpgsql.sql | 11 +++++--- 5 files changed, 68 insertions(+), 17 deletions(-) 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.