Allow the parameters to PL/PgSQL's RAISE statement to be expressions,

instead of just scalar variables. Add regression tests and update the
documentation. Along the way, remove some redundant error checking
code from exec_stmt_perform().

Original patch from Pavel Stehule, reworked by Neil Conway.
This commit is contained in:
Neil Conway 2005-06-14 06:43:15 +00:00
parent bd6bf50b03
commit d6636543c4
7 changed files with 126 additions and 91 deletions

View File

@ -1,5 +1,5 @@
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.71 2005/06/10 16:23:09 neilc Exp $ $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.72 2005/06/14 06:43:14 neilc Exp $
--> -->
<chapter id="plpgsql"> <chapter id="plpgsql">
@ -2533,9 +2533,9 @@ RAISE <replaceable class="parameter">level</replaceable> '<replaceable class="pa
<para> <para>
Inside the format string, <literal>%</literal> is replaced by the Inside the format string, <literal>%</literal> is replaced by the
next optional argument's string representation. Write next optional argument's string representation. Write
<literal>%%</literal> to emit a literal <literal>%</literal>. Note <literal>%%</literal> to emit a literal <literal>%</literal>.
that the optional arguments must presently be simple variables, Arguments can be simple variables or expressions,
not expressions, and the format must be a simple string literal. and the format must be a simple string literal.
</para> </para>
<!-- <!--

View File

@ -4,7 +4,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.75 2005/06/10 16:23:11 neilc Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.76 2005/06/14 06:43:14 neilc Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
@ -135,8 +135,8 @@ static void plpgsql_sql_error_callback(void *arg);
%type <exception> proc_exception %type <exception> proc_exception
%type <condition> proc_conditions %type <condition> proc_conditions
%type <list> raise_params
%type <ival> raise_level raise_param %type <ival> raise_level
%type <str> raise_msg %type <str> raise_msg
%type <list> getdiag_list %type <list> getdiag_list
@ -1157,23 +1157,10 @@ stmt_return_next: K_RETURN_NEXT lno
} }
; ;
stmt_raise : K_RAISE lno raise_level raise_msg raise_params ';' stmt_raise : K_RAISE lno raise_level raise_msg
{
PLpgSQL_stmt_raise *new;
new = palloc(sizeof(PLpgSQL_stmt_raise));
new->cmd_type = PLPGSQL_STMT_RAISE;
new->lineno = $2;
new->elog_level = $3;
new->message = $4;
new->params = $5;
$$ = (PLpgSQL_stmt *)new;
}
| K_RAISE lno raise_level raise_msg ';'
{ {
PLpgSQL_stmt_raise *new; PLpgSQL_stmt_raise *new;
int tok;
new = palloc(sizeof(PLpgSQL_stmt_raise)); new = palloc(sizeof(PLpgSQL_stmt_raise));
@ -1183,6 +1170,32 @@ stmt_raise : K_RAISE lno raise_level raise_msg raise_params ';'
new->message = $4; new->message = $4;
new->params = NIL; new->params = NIL;
tok = yylex();
/*
* We expect either a semi-colon, which
* indicates no parameters, or a comma that
* begins the list of parameter expressions
*/
if (tok != ',' && tok != ';')
yyerror("syntax error");
if (tok == ',')
{
PLpgSQL_expr *expr;
int term;
for (;;)
{
expr = read_sql_construct(',', ';', ", or ;",
"SELECT ",
true, true, &term);
new->params = lappend(new->params, expr);
if (term == ';')
break;
}
}
$$ = (PLpgSQL_stmt *)new; $$ = (PLpgSQL_stmt *)new;
} }
; ;
@ -1219,22 +1232,6 @@ raise_level : K_EXCEPTION
} }
; ;
raise_params : raise_params raise_param
{
$$ = lappend_int($1, $2);
}
| raise_param
{
$$ = list_make1_int($1);
}
;
raise_param : ',' T_SCALAR
{
$$ = yylval.scalar->dno;
}
;
loop_body : proc_sect K_END K_LOOP ';' loop_body : proc_sect K_END K_LOOP ';'
{ $$ = $1; } { $$ = $1; }
; ;
@ -1658,7 +1655,7 @@ read_sql_stmt(const char *sqlstart)
* expected: text to use in complaining that terminator was not found * expected: text to use in complaining that terminator was not found
* sqlstart: text to prefix to the accumulated SQL text * sqlstart: text to prefix to the accumulated SQL text
* isexpression: whether to say we're reading an "expression" or a "statement" * isexpression: whether to say we're reading an "expression" or a "statement"
* valid_sql: whether to check the syntax of the expression (plus sqlstart) * valid_sql: whether to check the syntax of the expr (prefixed with sqlstart)
* endtoken: if not NULL, ending token is stored at *endtoken * endtoken: if not NULL, ending token is stored at *endtoken
* (this is only interesting if until2 isn't zero) * (this is only interesting if until2 isn't zero)
*/ */

View File

@ -3,7 +3,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.143 2005/06/10 16:23:11 neilc Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.144 2005/06/14 06:43:14 neilc Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
@ -594,7 +594,7 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
error_context_stack = plerrcontext.previous; error_context_stack = plerrcontext.previous;
/* /*
* Return the triggers result * Return the trigger's result
*/ */
return rettup; return rettup;
} }
@ -1095,22 +1095,9 @@ static int
exec_stmt_perform(PLpgSQL_execstate *estate, PLpgSQL_stmt_perform *stmt) exec_stmt_perform(PLpgSQL_execstate *estate, PLpgSQL_stmt_perform *stmt)
{ {
PLpgSQL_expr *expr = stmt->expr; PLpgSQL_expr *expr = stmt->expr;
int rc;
/*
* If not already done create a plan for this expression
*/
if (expr->plan == NULL)
exec_prepare_plan(estate, expr);
rc = exec_run_select(estate, expr, 0, NULL);
if (rc != SPI_OK_SELECT)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("query \"%s\" did not return data", expr->query)));
(void) exec_run_select(estate, expr, 0, NULL);
exec_set_found(estate, (estate->eval_processed != 0)); exec_set_found(estate, (estate->eval_processed != 0));
exec_eval_cleanup(estate); exec_eval_cleanup(estate);
return PLPGSQL_RC_OK; return PLPGSQL_RC_OK;
@ -1941,15 +1928,18 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("too few parameters specified for RAISE"))); errmsg("too few parameters specified for RAISE")));
exec_eval_datum(estate, estate->datums[lfirst_int(current_param)], paramvalue = exec_eval_expr(estate,
InvalidOid, (PLpgSQL_expr *) lfirst(current_param),
&paramtypeid, &paramvalue, &paramisnull); &paramisnull,
&paramtypeid);
if (paramisnull) if (paramisnull)
extval = "<NULL>"; extval = "<NULL>";
else else
extval = convert_value_to_string(paramvalue, paramtypeid); extval = convert_value_to_string(paramvalue, paramtypeid);
plpgsql_dstring_append(&ds, extval); plpgsql_dstring_append(&ds, extval);
current_param = lnext(current_param); current_param = lnext(current_param);
exec_eval_cleanup(estate);
continue; continue;
} }

View File

@ -3,7 +3,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.42 2005/06/14 00:10:02 neilc Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.43 2005/06/14 06:43:14 neilc Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
@ -885,13 +885,20 @@ dump_return_next(PLpgSQL_stmt_return_next *stmt)
static void static void
dump_raise(PLpgSQL_stmt_raise *stmt) dump_raise(PLpgSQL_stmt_raise *stmt)
{ {
ListCell *l; ListCell *lc;
int i = 0;
dump_ind(); dump_ind();
printf("RAISE '%s'", stmt->message); printf("RAISE '%s'\n", stmt->message);
foreach (l, stmt->params) dump_indent += 2;
printf(" %d", lfirst_int(l)); foreach (lc, stmt->params)
printf("\n"); {
dump_ind();
printf(" parameter %d: ", i++);
dump_expr((PLpgSQL_expr *) lfirst(lc));
printf("\n");
}
dump_indent -= 2;
} }
static void static void
@ -916,7 +923,8 @@ dump_dynexecute(PLpgSQL_stmt_dynexecute *stmt)
{ {
dump_ind(); dump_ind();
printf(" target = %d %s\n", stmt->rec->recno, stmt->rec->refname); printf(" target = %d %s\n", stmt->rec->recno, stmt->rec->refname);
} else if (stmt->row != NULL) }
else if (stmt->row != NULL)
{ {
dump_ind(); dump_ind();
printf(" target = %d %s\n", stmt->row->rowno, stmt->row->refname); printf(" target = %d %s\n", stmt->row->rowno, stmt->row->refname);

View File

@ -3,7 +3,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.62 2005/06/10 16:23:11 neilc Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.63 2005/06/14 06:43:14 neilc Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
@ -515,7 +515,7 @@ typedef struct
int lineno; int lineno;
int elog_level; int elog_level;
char *message; char *message;
List *params; List *params; /* list of expressions */
} PLpgSQL_stmt_raise; } PLpgSQL_stmt_raise;

View File

@ -2418,17 +2418,17 @@ drop type eitype cascade;
-- --
-- SQLSTATE and SQLERRM test -- SQLSTATE and SQLERRM test
-- --
-- should fail: SQLSTATE and SQLERRM are only in defined EXCEPTION create function excpt_test1() returns void as $$
-- blocks
create function excpt_test() returns void as $$
begin begin
raise notice '% %', sqlstate, sqlerrm; raise notice '% %', sqlstate, sqlerrm;
end; $$ language plpgsql; end; $$ language plpgsql;
ERROR: syntax error at or near "sqlstate" at character 79 -- should fail: SQLSTATE and SQLERRM are only in defined EXCEPTION
LINE 3: raise notice '% %', sqlstate, sqlerrm; -- blocks
^ select excpt_test1();
-- should fail ERROR: column "sqlstate" does not exist
create function excpt_test() returns void as $$ CONTEXT: SQL statement "SELECT sqlstate"
PL/pgSQL function "excpt_test1" line 2 at raise
create function excpt_test2() returns void as $$
begin begin
begin begin
begin begin
@ -2436,10 +2436,12 @@ begin
end; end;
end; end;
end; $$ language plpgsql; end; $$ language plpgsql;
ERROR: syntax error at or near "sqlstate" at character 108 -- should fail
LINE 5: raise notice '% %', sqlstate, sqlerrm; select excpt_test2();
^ ERROR: column "sqlstate" does not exist
create function excpt_test() returns void as $$ CONTEXT: SQL statement "SELECT sqlstate"
PL/pgSQL function "excpt_test2" line 4 at raise
create function excpt_test3() returns void as $$
begin begin
begin begin
raise exception 'user exception'; raise exception 'user exception';
@ -2458,14 +2460,34 @@ begin
raise notice '% %', sqlstate, sqlerrm; raise notice '% %', sqlstate, sqlerrm;
end; end;
end; $$ language plpgsql; end; $$ language plpgsql;
select excpt_test(); select excpt_test3();
NOTICE: caught exception P0001 user exception NOTICE: caught exception P0001 user exception
NOTICE: P0001 user exception NOTICE: P0001 user exception
NOTICE: caught exception 22012 division by zero NOTICE: caught exception 22012 division by zero
NOTICE: P0001 user exception NOTICE: P0001 user exception
excpt_test excpt_test3
------------ -------------
(1 row) (1 row)
drop function excpt_test(); drop function excpt_test1();
drop function excpt_test2();
drop function excpt_test3();
-- parameters of raise stmt can be expressions
create function raise_exprs() returns void as $$
declare
a integer[] = '{10,20,30}';
c varchar = 'xyz';
i integer;
begin
i := 2;
raise notice '%; %; %; %; %; %', a, a[i], c, (select c || 'abc'), row(10,'aaa',NULL,30), NULL;
end;$$ language plpgsql;
select raise_exprs();
NOTICE: {10,20,30}; 20; xyz; xyzabc; (10,aaa,,30); <NULL>
raise_exprs
-------------
(1 row)
drop function raise_exprs();

View File

@ -2055,15 +2055,15 @@ drop type eitype cascade;
-- SQLSTATE and SQLERRM test -- SQLSTATE and SQLERRM test
-- --
-- should fail: SQLSTATE and SQLERRM are only in defined EXCEPTION create function excpt_test1() returns void as $$
-- blocks
create function excpt_test() returns void as $$
begin begin
raise notice '% %', sqlstate, sqlerrm; raise notice '% %', sqlstate, sqlerrm;
end; $$ language plpgsql; end; $$ language plpgsql;
-- should fail: SQLSTATE and SQLERRM are only in defined EXCEPTION
-- blocks
select excpt_test1();
-- should fail create function excpt_test2() returns void as $$
create function excpt_test() returns void as $$
begin begin
begin begin
begin begin
@ -2071,8 +2071,10 @@ begin
end; end;
end; end;
end; $$ language plpgsql; end; $$ language plpgsql;
-- should fail
select excpt_test2();
create function excpt_test() returns void as $$ create function excpt_test3() returns void as $$
begin begin
begin begin
raise exception 'user exception'; raise exception 'user exception';
@ -2092,5 +2094,21 @@ begin
end; end;
end; $$ language plpgsql; end; $$ language plpgsql;
select excpt_test(); select excpt_test3();
drop function excpt_test(); drop function excpt_test1();
drop function excpt_test2();
drop function excpt_test3();
-- parameters of raise stmt can be expressions
create function raise_exprs() returns void as $$
declare
a integer[] = '{10,20,30}';
c varchar = 'xyz';
i integer;
begin
i := 2;
raise notice '%; %; %; %; %; %', a, a[i], c, (select c || 'abc'), row(10,'aaa',NULL,30), NULL;
end;$$ language plpgsql;
select raise_exprs();
drop function raise_exprs();