1
0
mirror of https://github.com/postgres/postgres.git synced 2025-09-02 04:21:28 +03:00

Implement two new special variables in PL/PgSQL: SQLSTATE and SQLERRM.

These contain the SQLSTATE and error message of the current exception,
respectively. They are scope-local variables that are only defined
in exception handlers (so attempting to reference them outside an
exception handler is an error). Update the regression tests and the
documentation.

Also, do some minor related cleanup: export an unpack_sql_state()
function from the backend and use it to unpack a SQLSTATE into a
string, and add a free_var() function to pl_exec.c

Original patch from Pavel Stehule, review by Neil Conway.
This commit is contained in:
Neil Conway
2005-06-10 16:23:11 +00:00
parent 1a61896189
commit d46bc444ac
10 changed files with 226 additions and 51 deletions

View File

@@ -4,7 +4,7 @@
* procedural language
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.74 2005/06/08 00:49:36 neilc Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.75 2005/06/10 16:23:11 neilc Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -92,6 +92,7 @@ static void plpgsql_sql_error_callback(void *arg);
PLpgSQL_stmt_block *program;
PLpgSQL_condition *condition;
PLpgSQL_exception *exception;
PLpgSQL_exception_block *exception_block;
PLpgSQL_nsitem *nsitem;
PLpgSQL_diag_item *diagitem;
}
@@ -129,7 +130,8 @@ static void plpgsql_sql_error_callback(void *arg);
%type <stmt> stmt_dynexecute stmt_getdiag
%type <stmt> stmt_open stmt_fetch stmt_close stmt_null
%type <list> exception_sect proc_exceptions
%type <list> proc_exceptions
%type <exception_block> exception_sect
%type <exception> proc_exception
%type <condition> proc_conditions
@@ -1495,9 +1497,38 @@ execsql_start : T_WORD
;
exception_sect :
{ $$ = NIL; }
| K_EXCEPTION proc_exceptions
{ $$ = $2; }
{ $$ = NULL; }
| K_EXCEPTION lno
{
/*
* We use a mid-rule action to add these
* special variables to the namespace before
* parsing the WHEN clauses themselves.
*/
PLpgSQL_exception_block *new = palloc(sizeof(PLpgSQL_exception_block));
PLpgSQL_variable *var;
var = plpgsql_build_variable("sqlstate", $2,
plpgsql_build_datatype(TEXTOID, -1),
true);
((PLpgSQL_var *) var)->isconst = true;
new->sqlstate_varno = var->dno;
var = plpgsql_build_variable("sqlerrm", $2,
plpgsql_build_datatype(TEXTOID, -1),
true);
((PLpgSQL_var *) var)->isconst = true;
new->sqlerrm_varno = var->dno;
$<exception_block>$ = new;
}
proc_exceptions
{
PLpgSQL_exception_block *new = $<exception_block>3;
new->exc_list = $4;
$$ = new;
}
;
proc_exceptions : proc_exceptions proc_exception

View File

@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.90 2005/05/29 04:23:06 tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.91 2005/06/10 16:23:11 neilc Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -656,7 +656,7 @@ do_compile(FunctionCallInfo fcinfo,
if (num_out_args > 0 || function->fn_rettype == VOIDOID ||
function->fn_retset)
{
if (function->action->exceptions != NIL)
if (function->action->exceptions != NULL)
{
PLpgSQL_stmt_block *new;
@@ -882,7 +882,7 @@ plpgsql_parse_word(char *word)
}
/*
* Do a lookup on the compilers namestack
* Do a lookup on the compiler's namestack
*/
nse = plpgsql_ns_lookup(cp[0], NULL);
if (nse != NULL)
@@ -1935,7 +1935,7 @@ plpgsql_parse_err_condition(char *condname)
/* ----------
* plpgsql_adddatum Add a variable, record or row
* to the compilers datum list.
* to the compiler's datum list.
* ----------
*/
void

View File

@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.142 2005/06/07 02:47:17 neilc Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.143 2005/06/10 16:23:11 neilc Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -180,7 +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 void free_var(PLpgSQL_var *var);
/* ----------
* plpgsql_exec_function Called by the call handler for
@@ -760,12 +760,7 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
{
PLpgSQL_var *var = (PLpgSQL_var *) (estate->datums[n]);
if (var->freeval)
{
pfree((void *) (var->value));
var->freeval = false;
}
free_var(var);
if (!var->isconst || var->isnull)
{
if (var->default_val == NULL)
@@ -864,13 +859,37 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
SPI_restore_connection();
/* Look for a matching exception handler */
foreach (e, block->exceptions)
foreach (e, block->exceptions->exc_list)
{
PLpgSQL_exception *exception = (PLpgSQL_exception *) lfirst(e);
if (exception_matches_conditions(edata, exception->conditions))
{
/*
* Initialize the magic SQLSTATE and SQLERRM
* variables for the exception block. We needn't
* do this until we have found a matching
* exception.
*/
PLpgSQL_var *state_var;
PLpgSQL_var *errm_var;
state_var = (PLpgSQL_var *) (estate->datums[block->exceptions->sqlstate_varno]);
state_var->value = DirectFunctionCall1(textin,
CStringGetDatum(unpack_sql_state(edata->sqlerrcode)));
state_var->freeval = true;
state_var->isnull = false;
errm_var = (PLpgSQL_var *) (estate->datums[block->exceptions->sqlerrm_varno]);
errm_var->value = DirectFunctionCall1(textin,
CStringGetDatum(edata->message));
errm_var->freeval = true;
errm_var->isnull = false;
rc = exec_stmts(estate, exception->action);
free_var(state_var);
free_var(errm_var);
break;
}
}
@@ -2586,9 +2605,7 @@ exec_stmt_open(PLpgSQL_execstate *estate, PLpgSQL_stmt_open *stmt)
* Store the eventually assigned cursor name in the cursor variable
* ----------
*/
if (curvar->freeval)
pfree((void *) (curvar->value));
free_var(curvar);
curvar->value = DirectFunctionCall1(textin, CStringGetDatum(portal->name));
curvar->isnull = false;
curvar->freeval = true;
@@ -2684,9 +2701,7 @@ exec_stmt_open(PLpgSQL_execstate *estate, PLpgSQL_stmt_open *stmt)
* Store the eventually assigned portal name in the cursor variable
* ----------
*/
if (curvar->freeval)
pfree((void *) (curvar->value));
free_var(curvar);
curvar->value = DirectFunctionCall1(textin, CStringGetDatum(portal->name));
curvar->isnull = false;
curvar->freeval = true;
@@ -2857,11 +2872,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
errmsg("NULL cannot be assigned to variable \"%s\" declared NOT NULL",
var->refname)));
if (var->freeval)
{
pfree(DatumGetPointer(var->value));
var->freeval = false;
}
free_var(var);
/*
* If type is by-reference, make sure we have a freshly
@@ -4343,3 +4354,13 @@ plpgsql_xact_cb(XactEvent event, void *arg)
FreeExecutorState(simple_eval_estate);
simple_eval_estate = NULL;
}
static void
free_var(PLpgSQL_var *var)
{
if (var->freeval)
{
pfree(DatumGetPointer(var->value));
var->freeval = false;
}
}

View File

@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.40 2005/04/05 06:22:16 tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.41 2005/06/10 16:23:11 neilc Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -634,7 +634,7 @@ dump_block(PLpgSQL_stmt_block *block)
{
ListCell *e;
foreach (e, block->exceptions)
foreach (e, block->exceptions->exc_list)
{
PLpgSQL_exception *exc = (PLpgSQL_exception *) lfirst(e);
PLpgSQL_condition *cond;

View File

@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.61 2005/06/07 02:47:18 neilc Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.62 2005/06/10 16:23:11 neilc Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -322,6 +322,13 @@ typedef struct PLpgSQL_condition
struct PLpgSQL_condition *next;
} PLpgSQL_condition;
typedef struct
{
int sqlstate_varno;
int sqlerrm_varno;
List *exc_list; /* List of WHEN clauses */
} PLpgSQL_exception_block;
typedef struct
{ /* One EXCEPTION ... WHEN clause */
int lineno;
@@ -336,9 +343,9 @@ typedef struct
int lineno;
char *label;
List *body; /* List of statements */
List *exceptions; /* List of WHEN clauses */
int n_initvars;
int *initvarnos;
PLpgSQL_exception_block *exceptions;
} PLpgSQL_stmt_block;