Add PL/pgSQL SQLSTATE and SQLERRM support which sets these values on
error. Pavel Stehule
This commit is contained in:
parent
8c792fe9cb
commit
38af680ad5
@ -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> <<<replaceable>label</replaceable>>> </optional>
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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
|
||||
*/
|
||||
@ -856,6 +871,16 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
|
||||
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,
|
||||
* it will have left us in a disconnected state. We need this
|
||||
@ -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
|
||||
|
@ -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.
|
||||
*
|
||||
@ -339,6 +339,8 @@ typedef struct
|
||||
List *exceptions; /* List of WHEN clauses */
|
||||
int n_initvars;
|
||||
int *initvarnos;
|
||||
int sqlstate_varno;
|
||||
int sqlerrm_varno;
|
||||
} PLpgSQL_stmt_block;
|
||||
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user