Add PL/pgSQL SQLSTATE and SQLERRM support which sets these values on

error.

Pavel Stehule
This commit is contained in:
Bruce Momjian 2005-05-26 00:16:31 +00:00
parent 8c792fe9cb
commit 38af680ad5
6 changed files with 133 additions and 17 deletions

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.67 2005/04/19 03:55:43 momjian Exp $
$PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.68 2005/05/26 00:16:31 momjian Exp $
-->
<chapter id="plpgsql">
@ -2007,12 +2007,13 @@ END LOOP;
</indexterm>
<para>
By default, any error occurring in a <application>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 <command>BEGIN</> block with an
<literal>EXCEPTION</> clause. The syntax is an extension of the
normal syntax for a <command>BEGIN</> block:
Any error occurring in <application>PL/pgSQL</> sets variables
<varname>SQLSTATE</> and <varname>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 <command>BEGIN</> block with an <literal>EXCEPTION</>
clause. The syntax is an extension of the normal syntax for a
<command>BEGIN</> block:
<synopsis>
<optional> &lt;&lt;<replaceable>label</replaceable>&gt;&gt; </optional>

View File

@ -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> fict_vars_sect
%type <declhdr> decl_sect
%type <varname> decl_varname
%type <str> 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
{

View File

@ -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

View File

@ -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;

View File

@ -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();

View File

@ -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();