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">
|
<chapter id="plpgsql">
|
||||||
@ -2007,12 +2007,13 @@ END LOOP;
|
|||||||
</indexterm>
|
</indexterm>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
By default, any error occurring in a <application>PL/pgSQL</>
|
Any error occurring in <application>PL/pgSQL</> sets variables
|
||||||
function aborts execution of the function, and indeed of the
|
<varname>SQLSTATE</> and <varname>SQLERRM</>, and, by default,
|
||||||
surrounding transaction as well. You can trap errors and recover
|
aborts execution of the function, and indeed of the surrounding
|
||||||
from them by using a <command>BEGIN</> block with an
|
transaction as well. You can trap errors and recover from them by
|
||||||
<literal>EXCEPTION</> clause. The syntax is an extension of the
|
using a <command>BEGIN</> block with an <literal>EXCEPTION</>
|
||||||
normal syntax for a <command>BEGIN</> block:
|
clause. The syntax is an extension of the normal syntax for a
|
||||||
|
<command>BEGIN</> block:
|
||||||
|
|
||||||
<synopsis>
|
<synopsis>
|
||||||
<optional> <<<replaceable>label</replaceable>>> </optional>
|
<optional> <<<replaceable>label</replaceable>>> </optional>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
* procedural language
|
* procedural language
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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.
|
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||||
*
|
*
|
||||||
@ -80,6 +80,11 @@ static void plpgsql_sql_error_callback(void *arg);
|
|||||||
int n_initvars;
|
int n_initvars;
|
||||||
int *initvarnos;
|
int *initvarnos;
|
||||||
} declhdr;
|
} declhdr;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
int sqlstate_varno;
|
||||||
|
int sqlerrm_varno;
|
||||||
|
} fict_vars;
|
||||||
List *list;
|
List *list;
|
||||||
PLpgSQL_type *dtype;
|
PLpgSQL_type *dtype;
|
||||||
PLpgSQL_datum *scalar; /* a VAR, RECFIELD, or TRIGARG */
|
PLpgSQL_datum *scalar; /* a VAR, RECFIELD, or TRIGARG */
|
||||||
@ -96,6 +101,7 @@ static void plpgsql_sql_error_callback(void *arg);
|
|||||||
PLpgSQL_diag_item *diagitem;
|
PLpgSQL_diag_item *diagitem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
%type <fict_vars> fict_vars_sect
|
||||||
%type <declhdr> decl_sect
|
%type <declhdr> decl_sect
|
||||||
%type <varname> decl_varname
|
%type <varname> decl_varname
|
||||||
%type <str> decl_renname
|
%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;
|
PLpgSQL_stmt_block *new;
|
||||||
|
|
||||||
new = palloc0(sizeof(PLpgSQL_stmt_block));
|
new = palloc0(sizeof(PLpgSQL_stmt_block));
|
||||||
|
|
||||||
new->cmd_type = PLPGSQL_STMT_BLOCK;
|
new->cmd_type = PLPGSQL_STMT_BLOCK;
|
||||||
new->lineno = $3;
|
new->lineno = $4;
|
||||||
new->label = $1.label;
|
new->label = $1.label;
|
||||||
new->n_initvars = $1.n_initvars;
|
new->n_initvars = $1.n_initvars;
|
||||||
new->initvarnos = $1.initvarnos;
|
new->initvarnos = $1.initvarnos;
|
||||||
new->body = $4;
|
new->body = $5;
|
||||||
new->exceptions = $5;
|
new->exceptions = $6;
|
||||||
|
|
||||||
|
new->sqlstate_varno = $2.sqlstate_varno;
|
||||||
|
new->sqlerrm_varno = $2.sqlerrm_varno;
|
||||||
|
|
||||||
plpgsql_ns_pop();
|
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
|
decl_sect : opt_label
|
||||||
{
|
{
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* procedural language
|
* procedural language
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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.
|
* 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 void exec_init_tuple_store(PLpgSQL_execstate *estate);
|
||||||
static bool compatible_tupdesc(TupleDesc td1, TupleDesc td2);
|
static bool compatible_tupdesc(TupleDesc td1, TupleDesc td2);
|
||||||
static void exec_set_found(PLpgSQL_execstate *estate, bool state);
|
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 i;
|
||||||
int n;
|
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
|
* First initialize all variables declared in this block
|
||||||
*/
|
*/
|
||||||
@ -855,6 +870,16 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
|
|||||||
RollbackAndReleaseCurrentSubTransaction();
|
RollbackAndReleaseCurrentSubTransaction();
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
CurrentResourceOwner = oldowner;
|
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,
|
* 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;
|
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
|
* exec_stmts Iterate over a list of statements
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* procedural language
|
* procedural language
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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.
|
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||||
*
|
*
|
||||||
@ -336,9 +336,11 @@ typedef struct
|
|||||||
int lineno;
|
int lineno;
|
||||||
char *label;
|
char *label;
|
||||||
List *body; /* List of statements */
|
List *body; /* List of statements */
|
||||||
List *exceptions; /* List of WHEN clauses */
|
List *exceptions; /* List of WHEN clauses */
|
||||||
int n_initvars;
|
int n_initvars;
|
||||||
int *initvarnos;
|
int *initvarnos;
|
||||||
|
int sqlstate_varno;
|
||||||
|
int sqlerrm_varno;
|
||||||
} PLpgSQL_stmt_block;
|
} PLpgSQL_stmt_block;
|
||||||
|
|
||||||
|
|
||||||
|
@ -2380,3 +2380,29 @@ ERROR: control reached end of function without RETURN
|
|||||||
CONTEXT: PL/pgSQL function "missing_return_expr"
|
CONTEXT: PL/pgSQL function "missing_return_expr"
|
||||||
drop function void_return_expr();
|
drop function void_return_expr();
|
||||||
drop function missing_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 void_return_expr();
|
||||||
drop function missing_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