diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml index 0fe722cb887..17e62744c5e 100644 --- a/doc/src/sgml/plpgsql.sgml +++ b/doc/src/sgml/plpgsql.sgml @@ -1,5 +1,5 @@ @@ -2007,12 +2007,13 @@ END LOOP; - By default, any error occurring in a PL/pgSQL - function aborts execution of the function, and indeed of the - surrounding transaction as well. You can trap errors and recover - from them by using a BEGIN block with an - EXCEPTION clause. The syntax is an extension of the - normal syntax for a BEGIN block: + Any error occurring in PL/pgSQL sets variables + SQLSTATE and SQLERRM, and, by default, + aborts execution of the function, and indeed of the surrounding + transaction as well. You can trap errors and recover from them by + using a BEGIN block with an EXCEPTION + clause. The syntax is an extension of the normal syntax for a + BEGIN block: <<label>> diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y index 8b5f6b5e28d..d4a535d548b 100644 --- a/src/pl/plpgsql/src/gram.y +++ b/src/pl/plpgsql/src/gram.y @@ -4,7 +4,7 @@ * procedural language * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.69 2005/04/07 14:53:04 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.70 2005/05/26 00:16:31 momjian Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -80,6 +80,11 @@ static void plpgsql_sql_error_callback(void *arg); int n_initvars; int *initvarnos; } declhdr; + struct + { + int sqlstate_varno; + int sqlerrm_varno; + } fict_vars; List *list; PLpgSQL_type *dtype; PLpgSQL_datum *scalar; /* a VAR, RECFIELD, or TRIGARG */ @@ -96,6 +101,7 @@ static void plpgsql_sql_error_callback(void *arg); PLpgSQL_diag_item *diagitem; } +%type fict_vars_sect %type decl_sect %type decl_varname %type decl_renname @@ -244,19 +250,22 @@ opt_semi : | ';' ; -pl_block : decl_sect K_BEGIN lno proc_sect exception_sect K_END +pl_block : decl_sect fict_vars_sect K_BEGIN lno proc_sect exception_sect K_END { PLpgSQL_stmt_block *new; new = palloc0(sizeof(PLpgSQL_stmt_block)); new->cmd_type = PLPGSQL_STMT_BLOCK; - new->lineno = $3; + new->lineno = $4; new->label = $1.label; new->n_initvars = $1.n_initvars; new->initvarnos = $1.initvarnos; - new->body = $4; - new->exceptions = $5; + new->body = $5; + new->exceptions = $6; + + new->sqlstate_varno = $2.sqlstate_varno; + new->sqlerrm_varno = $2.sqlerrm_varno; plpgsql_ns_pop(); @@ -264,6 +273,19 @@ pl_block : decl_sect K_BEGIN lno proc_sect exception_sect K_END } ; +fict_vars_sect : + { + PLpgSQL_variable *var; + + plpgsql_ns_setlocal(false); + var = plpgsql_build_variable("sqlstate", 0, + plpgsql_build_datatype(TEXTOID, -1), true); + $$.sqlstate_varno = var->dno; + var = plpgsql_build_variable("sqlerrm", 0, + plpgsql_build_datatype(TEXTOID, -1), true); + $$.sqlerrm_varno = var->dno; + plpgsql_add_initdatums(NULL); + }; decl_sect : opt_label { diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 18bcd4fe2ee..333ca21c0ba 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.138 2005/05/06 17:24:55 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.139 2005/05/26 00:16:31 momjian Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -180,6 +180,7 @@ static Datum exec_simple_cast_value(Datum value, Oid valtype, static void exec_init_tuple_store(PLpgSQL_execstate *estate); static bool compatible_tupdesc(TupleDesc td1, TupleDesc td2); static void exec_set_found(PLpgSQL_execstate *estate, bool state); +static char *unpack_sql_state(int ssval); /* ---------- @@ -747,6 +748,20 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block) int i; int n; + + /* setup SQLSTATE and SQLERRM */ + PLpgSQL_var *var; + + var = (PLpgSQL_var *) (estate->datums[block->sqlstate_varno]); + var->isnull = false; + var->freeval = true; + var->value = DirectFunctionCall1(textin, CStringGetDatum("00000")); + + var = (PLpgSQL_var *) (estate->datums[block->sqlerrm_varno]); + var->isnull = false; + var->freeval = true; + var->value = DirectFunctionCall1(textin, CStringGetDatum("Sucessful completion")); + /* * First initialize all variables declared in this block */ @@ -855,6 +870,16 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block) RollbackAndReleaseCurrentSubTransaction(); MemoryContextSwitchTo(oldcontext); CurrentResourceOwner = oldowner; + + /* set SQLSTATE and SQLERRM variables */ + + var = (PLpgSQL_var *) (estate->datums[block->sqlstate_varno]); + pfree((void *) (var->value)); + var->value = DirectFunctionCall1(textin, CStringGetDatum(unpack_sql_state(edata->sqlerrcode))); + + var = (PLpgSQL_var *) (estate->datums[block->sqlerrm_varno]); + pfree((void *) (var->value)); + var->value = DirectFunctionCall1(textin, CStringGetDatum(edata->message)); /* * If AtEOSubXact_SPI() popped any SPI context of the subxact, @@ -919,6 +944,26 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block) return PLPGSQL_RC_OK; } +/* + * unpack MAKE_SQLSTATE code + * This code is copied from backend/utils/error/elog.c. + */ +static char * +unpack_sql_state(int ssval) +{ + static char tbuf[12]; + int i; + + for (i = 0; i < 5; i++) + { + tbuf[i] = PGUNSIXBIT(ssval); + ssval >>= 6; + } + tbuf[i] = '\0'; + return tbuf; +} + + /* ---------- * exec_stmts Iterate over a list of statements diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index 80bcd9e8fa6..f3b14fdf643 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.58 2005/04/05 06:22:16 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.59 2005/05/26 00:16:31 momjian Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -336,9 +336,11 @@ typedef struct int lineno; char *label; List *body; /* List of statements */ - List *exceptions; /* List of WHEN clauses */ - int n_initvars; - int *initvarnos; + List *exceptions; /* List of WHEN clauses */ + int n_initvars; + int *initvarnos; + int sqlstate_varno; + int sqlerrm_varno; } PLpgSQL_stmt_block; diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out index 08fbe46b3a2..5ee5c338e46 100644 --- a/src/test/regress/expected/plpgsql.out +++ b/src/test/regress/expected/plpgsql.out @@ -2380,3 +2380,29 @@ ERROR: control reached end of function without RETURN CONTEXT: PL/pgSQL function "missing_return_expr" drop function void_return_expr(); drop function missing_return_expr(); +-- test SQLSTATE and SQLERRM +create or replace function trap_exceptions() returns void as $_$ +begin + begin + raise exception 'first exception'; + exception when others then + raise notice '% %', SQLSTATE, SQLERRM; + end; + raise notice '% %', SQLSTATE, SQLERRM; + begin + raise exception 'last exception'; + exception when others then + raise notice '% %', SQLSTATE, SQLERRM; + end; + return; +end; $_$ language plpgsql; +select trap_exceptions(); +NOTICE: P0001 first exception +NOTICE: 00000 Sucessful completion +NOTICE: P0001 last exception + trap_exceptions +----------------- + +(1 row) + +drop function trap_exceptions(); diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql index 7ea7c8c6e0c..e2ef2ec5fe3 100644 --- a/src/test/regress/sql/plpgsql.sql +++ b/src/test/regress/sql/plpgsql.sql @@ -2018,3 +2018,23 @@ select missing_return_expr(); drop function void_return_expr(); drop function missing_return_expr(); +-- test SQLSTATE and SQLERRM +create or replace function trap_exceptions() returns void as $_$ +begin + begin + raise exception 'first exception'; + exception when others then + raise notice '% %', SQLSTATE, SQLERRM; + end; + raise notice '% %', SQLSTATE, SQLERRM; + begin + raise exception 'last exception'; + exception when others then + raise notice '% %', SQLSTATE, SQLERRM; + end; + return; +end; $_$ language plpgsql; + +select trap_exceptions(); + +drop function trap_exceptions();