plpgsql does exceptions.

There are still some things that need refinement; in particular I fear
that the recognized set of error condition names probably has little in
common with what Oracle recognizes.  But it's a start.
This commit is contained in:
Tom Lane 2004-07-31 07:39:21 +00:00
parent b5d2821929
commit beda4814c1
11 changed files with 767 additions and 110 deletions

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.41 2004/07/11 23:26:51 momjian Exp $
$PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.42 2004/07/31 07:39:17 tgl Exp $
-->
<chapter id="plpgsql">
@ -1796,6 +1796,101 @@ END LOOP;
rather than the simple syntax error one might expect to get.
</para>
</note>
</sect2>
<sect2 id="plpgsql-error-trapping">
<title>Trapping Errors</title>
<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:
<synopsis>
<optional> &lt;&lt;<replaceable>label</replaceable>&gt;&gt; </optional>
<optional> DECLARE
<replaceable>declarations</replaceable> </optional>
BEGIN
<replaceable>statements</replaceable>
EXCEPTION
WHEN <replaceable>condition</replaceable> THEN
<replaceable>handler_statements</replaceable>
<optional> WHEN <replaceable>condition</replaceable> THEN
<replaceable>handler_statements</replaceable>
...
</optional>
END;
</synopsis>
</para>
<para>
If no error occurs, this form of block simply executes all the
<replaceable>statements</replaceable>, and then control passes
to the next statement after <literal>END</>. But if an error
occurs within the <replaceable>statements</replaceable>, further
processing of the <replaceable>statements</replaceable> is
abandoned, and control passes to the <literal>EXCEPTION</> list.
The list is searched for the first <replaceable>condition</replaceable>
matching the error that occurred. If a match is found, the
corresponding <replaceable>handler_statements</replaceable> are
executed, and then control passes to the next statement after
<literal>END</>. If no match is found, the error propagates out
as though the <literal>EXCEPTION</> clause were not there at all:
the error can be caught by an enclosing block with
<literal>EXCEPTION</>, or if there is none it aborts processing
of the function. The special condition name <literal>OTHERS</>
matches every error type except <literal>QUERY_CANCELED</>.
(It is possible, but usually not a good idea, to trap
<literal>QUERY_CANCELED</> by name.)
</para>
<para>
If a new error occurs within the selected
<replaceable>handler_statements</replaceable>, it cannot be caught
by this <literal>EXCEPTION</> clause, but is propagated out.
A surrounding <literal>EXCEPTION</> clause could catch it.
</para>
<para>
When an error is caught by an <literal>EXCEPTION</> clause,
the local variables of the <application>PL/pgSQL</> function
remain as they were when the error occurred, but all changes
to persistent database state within the block are rolled back.
As an example, consider this fragment:
<programlisting>
INSERT INTO mytab(firstname, lastname) VALUES('Tom', 'Jones');
BEGIN
UPDATE mytab SET firstname = 'Joe' WHERE lastname = 'Jones';
x := x + 1;
y := x / 0;
EXCEPTION
WHEN division_by_zero THEN
RAISE NOTICE 'caught division_by_zero';
RETURN x;
END;
</programlisting>
When control reaches the assignment to <literal>y</>, it will
fail with a <literal>division_by_zero</> error. This will be caught by
the <literal>EXCEPTION</> clause. The value returned in the
<command>RETURN</> statement will be the incremented value of
<literal>x</>, but the effects of the <command>UPDATE</> command will
have been rolled back. The <command>INSERT</> command is not rolled
back, however, so the end result is that the database contains
<literal>Tom Jones</> not <literal>Joe Jones</>.
</para>
<tip>
<para>
A block containing an <literal>EXCEPTION</> clause is significantly
more expensive to enter and exit than a block without one. Therefore,
don't use <literal>EXCEPTION</> without need.
</para>
</tip>
</sect2>
</sect1>
@ -2120,11 +2215,11 @@ RAISE <replaceable class="parameter">level</replaceable> '<replaceable class="pa
</synopsis>
Possible levels are <literal>DEBUG</literal>,
<literal>LOG</literal>,
<literal>LOG</literal>, <literal>INFO</literal>,
<literal>NOTICE</literal>, <literal>WARNING</literal>,
and <literal>EXCEPTION</literal>.
<literal>EXCEPTION</literal> raises an error and aborts the current
transaction; the other levels only generate messages of different
<literal>EXCEPTION</literal> raises an error (which normally aborts the
current transaction); the other levels only generate messages of different
priority levels.
Whether messages of a particular priority are reported to the client,
written to the server log, or both is controlled by the
@ -2164,28 +2259,11 @@ RAISE EXCEPTION 'Nonexistent ID --> %', user_id;
</para>
<para>
<productname>PostgreSQL</productname> does not have a very smart
exception handling model. Whenever the parser, planner/optimizer
or executor decide that a statement cannot be processed any longer,
the whole transaction gets aborted and the system jumps back
into the main loop to get the next command from the client application.
</para>
<para>
It is possible to hook into the error mechanism to notice that this
happens. But currently it is impossible to tell what really
caused the abort (data type format error, floating-point
error, parse error, etc.). And it is possible that the database server
is in an inconsistent state at this point so returning to the upper
executor or issuing more commands might corrupt the whole database.
</para>
<para>
Thus, the only thing <application>PL/pgSQL</application>
currently does when it encounters an abort during execution of a
function or trigger procedure is to add some fields to the message
telling in which function and where (line number and type of statement)
the error happened. The error always stops execution of the function.
<command>RAISE EXCEPTION</command> presently always generates
the same SQLSTATE code, <literal>P0001</>, no matter what message
it is invoked with. It is possible to trap this exception with
<literal>EXCEPTION ... WHEN RAISE_EXCEPTION THEN ...</> but there
is no way to tell one <command>RAISE</> from another.
</para>
</sect1>

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.173 2004/07/28 14:23:27 tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.174 2004/07/31 07:39:18 tgl Exp $
*
* NOTES
* Transaction aborts can now occur two ways:
@ -1589,7 +1589,8 @@ CleanupTransaction(void)
* State should still be TRANS_ABORT from AbortTransaction().
*/
if (s->state != TRANS_ABORT)
elog(FATAL, "CleanupTransaction and not in abort state");
elog(FATAL, "CleanupTransaction: unexpected state %s",
TransStateAsString(s->state));
/*
* do abort cleanup processing
@ -1773,7 +1774,7 @@ CommitTransactionCommand(void)
/*
* We were just issued a SAVEPOINT inside a transaction block.
* Start a subtransaction. (BeginTransactionBlock already
* Start a subtransaction. (DefineSavepoint already
* did PushTransaction, so as to have someplace to put the
* SUBBEGIN state.)
*/
@ -1853,6 +1854,7 @@ CleanupAbortedSubTransactions(bool returnName)
AssertState(PointerIsValid(s->parent));
Assert(s->parent->blockState == TBLOCK_SUBINPROGRESS ||
s->parent->blockState == TBLOCK_INPROGRESS ||
s->parent->blockState == TBLOCK_STARTED ||
s->parent->blockState == TBLOCK_SUBABORT_PENDING);
/*
@ -1878,7 +1880,8 @@ CleanupAbortedSubTransactions(bool returnName)
}
AssertState(s->blockState == TBLOCK_SUBINPROGRESS ||
s->blockState == TBLOCK_INPROGRESS);
s->blockState == TBLOCK_INPROGRESS ||
s->blockState == TBLOCK_STARTED);
return name;
}
@ -2468,7 +2471,7 @@ DefineSavepoint(char *name)
case TBLOCK_SUBABORT_PENDING:
case TBLOCK_SUBENDABORT_RELEASE:
case TBLOCK_SUBEND:
elog(FATAL, "BeginTransactionBlock: unexpected state %s",
elog(FATAL, "DefineSavepoint: unexpected state %s",
BlockStateAsString(s->blockState));
break;
}
@ -2657,20 +2660,126 @@ RollbackToSavepoint(List *options)
}
/*
* RollbackAndReleaseSavepoint
* BeginInternalSubTransaction
* This is the same as DefineSavepoint except it allows TBLOCK_STARTED
* state, and therefore it can safely be used in a function that might
* be called when not inside a BEGIN block. Also, we automatically
* cycle through CommitTransactionCommand/StartTransactionCommand
* instead of expecting the caller to do it.
*
* Executes a ROLLBACK TO command, immediately followed by a RELEASE
* of the same savepoint.
* Optionally, name can be NULL to create an unnamed savepoint.
*/
void
RollbackAndReleaseSavepoint(List *options)
BeginInternalSubTransaction(char *name)
{
TransactionState s;
TransactionState s = CurrentTransactionState;
RollbackToSavepoint(options);
s = CurrentTransactionState;
Assert(s->blockState == TBLOCK_SUBENDABORT);
switch (s->blockState)
{
case TBLOCK_STARTED:
case TBLOCK_INPROGRESS:
case TBLOCK_SUBINPROGRESS:
/* Normal subtransaction start */
PushTransaction();
s = CurrentTransactionState; /* changed by push */
/*
* Note that we are allocating the savepoint name in the
* parent transaction's CurTransactionContext, since we
* don't yet have a transaction context for the new guy.
*/
if (name)
s->name = MemoryContextStrdup(CurTransactionContext, name);
s->blockState = TBLOCK_SUBBEGIN;
break;
/* These cases are invalid. Reject them altogether. */
case TBLOCK_DEFAULT:
case TBLOCK_BEGIN:
case TBLOCK_SUBBEGIN:
case TBLOCK_ABORT:
case TBLOCK_SUBABORT:
case TBLOCK_ENDABORT:
case TBLOCK_END:
case TBLOCK_SUBENDABORT_ALL:
case TBLOCK_SUBENDABORT:
case TBLOCK_SUBABORT_PENDING:
case TBLOCK_SUBENDABORT_RELEASE:
case TBLOCK_SUBEND:
elog(FATAL, "BeginInternalSubTransaction: unexpected state %s",
BlockStateAsString(s->blockState));
break;
}
CommitTransactionCommand();
StartTransactionCommand();
}
/*
* ReleaseCurrentSubTransaction
*
* RELEASE (ie, commit) the innermost subtransaction, regardless of its
* savepoint name (if any).
* NB: do NOT use CommitTransactionCommand/StartTransactionCommand with this.
*/
void
ReleaseCurrentSubTransaction(void)
{
TransactionState s = CurrentTransactionState;
if (s->blockState != TBLOCK_SUBINPROGRESS)
elog(ERROR, "ReleaseCurrentSubTransaction: unexpected state %s",
BlockStateAsString(s->blockState));
MemoryContextSwitchTo(CurTransactionContext);
CommitTransactionToLevel(GetCurrentTransactionNestLevel());
}
/*
* RollbackAndReleaseCurrentSubTransaction
*
* ROLLBACK and RELEASE (ie, abort) the innermost subtransaction, regardless
* of its savepoint name (if any).
* NB: do NOT use CommitTransactionCommand/StartTransactionCommand with this.
*/
void
RollbackAndReleaseCurrentSubTransaction(void)
{
TransactionState s = CurrentTransactionState;
switch (s->blockState)
{
/* Must be in a subtransaction */
case TBLOCK_SUBABORT:
case TBLOCK_SUBINPROGRESS:
break;
/* these cases are invalid. */
case TBLOCK_DEFAULT:
case TBLOCK_STARTED:
case TBLOCK_ABORT:
case TBLOCK_INPROGRESS:
case TBLOCK_BEGIN:
case TBLOCK_END:
case TBLOCK_ENDABORT:
case TBLOCK_SUBEND:
case TBLOCK_SUBENDABORT_ALL:
case TBLOCK_SUBENDABORT:
case TBLOCK_SUBABORT_PENDING:
case TBLOCK_SUBENDABORT_RELEASE:
case TBLOCK_SUBBEGIN:
elog(FATAL, "RollbackAndReleaseCurrentSubTransaction: unexpected state %s",
BlockStateAsString(s->blockState));
break;
}
/*
* Abort the current subtransaction, if needed.
*/
if (s->blockState == TBLOCK_SUBINPROGRESS)
AbortSubTransaction();
s->blockState = TBLOCK_SUBENDABORT_RELEASE;
/* And clean it up, too */
CleanupAbortedSubTransactions(false);
}
/*
@ -2748,7 +2857,7 @@ AbortOutOfAnyTransaction(void)
* Commit everything from the current transaction level
* up to the specified level (inclusive).
*/
void
static void
CommitTransactionToLevel(int level)
{
TransactionState s = CurrentTransactionState;

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/access/xact.h,v 1.67 2004/07/27 05:11:24 tgl Exp $
* $PostgreSQL: pgsql/src/include/access/xact.h,v 1.68 2004/07/31 07:39:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -107,7 +107,9 @@ extern void UserAbortTransactionBlock(void);
extern void ReleaseSavepoint(List *options);
extern void DefineSavepoint(char *name);
extern void RollbackToSavepoint(List *options);
extern void RollbackAndReleaseSavepoint(List *options);
extern void BeginInternalSubTransaction(char *name);
extern void ReleaseCurrentSubTransaction(void);
extern void RollbackAndReleaseCurrentSubTransaction(void);
extern bool IsSubTransaction(void);
extern bool IsTransactionBlock(void);
extern bool IsTransactionOrTransactionBlock(void);

View File

@ -11,7 +11,7 @@
*
* Copyright (c) 2003, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/include/utils/errcodes.h,v 1.13 2004/07/27 05:11:35 tgl Exp $
* $PostgreSQL: pgsql/src/include/utils/errcodes.h,v 1.14 2004/07/31 07:39:20 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -326,6 +326,10 @@
#define ERRCODE_CONFIG_FILE_ERROR MAKE_SQLSTATE('F','0', '0','0','0')
#define ERRCODE_LOCK_FILE_EXISTS MAKE_SQLSTATE('F','0', '0','0','1')
/* Class P0 - PL/pgSQL Error (PostgreSQL-specific error class) */
#define ERRCODE_PLPGSQL_ERROR MAKE_SQLSTATE('P','0', '0','0','0')
#define ERRCODE_RAISE_EXCEPTION MAKE_SQLSTATE('P','0', '0','0','1')
/* Class XX - Internal Error (PostgreSQL-specific error class) */
/* (this is for "can't-happen" conditions and software bugs) */
#define ERRCODE_INTERNAL_ERROR MAKE_SQLSTATE('X','X', '0','0','0')

View File

@ -4,7 +4,7 @@
* procedural language
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.57 2004/07/04 02:49:04 tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.58 2004/07/31 07:39:20 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@ -96,6 +96,8 @@ static void check_assignable(PLpgSQL_datum *datum);
PLpgSQL_stmt *stmt;
PLpgSQL_stmts *stmts;
PLpgSQL_stmt_block *program;
PLpgSQL_exception *exception;
PLpgSQL_exceptions *exceptions;
PLpgSQL_nsitem *nsitem;
}
@ -131,6 +133,9 @@ static void check_assignable(PLpgSQL_datum *datum);
%type <stmt> stmt_dynexecute stmt_getdiag
%type <stmt> stmt_open stmt_fetch stmt_close
%type <exceptions> exception_sect proc_exceptions
%type <exception> proc_exception
%type <intlist> raise_params
%type <ival> raise_level raise_param
%type <str> raise_msg
@ -240,7 +245,7 @@ opt_semi :
| ';'
;
pl_block : decl_sect K_BEGIN lno proc_sect K_END
pl_block : decl_sect K_BEGIN lno proc_sect exception_sect K_END
{
PLpgSQL_stmt_block *new;
@ -253,6 +258,7 @@ pl_block : decl_sect K_BEGIN lno proc_sect K_END
new->n_initvars = $1.n_initvars;
new->initvarnos = $1.initvarnos;
new->body = $4;
new->exceptions = $5;
plpgsql_ns_pop();
@ -588,7 +594,7 @@ proc_stmts : proc_stmts proc_stmt
$1->stmts_alloc *= 2;
$1->stmts = realloc($1->stmts, sizeof(PLpgSQL_stmt *) * $1->stmts_alloc);
}
$1->stmts[$1->stmts_used++] = (struct PLpgSQL_stmt *)$2;
$1->stmts[$1->stmts_used++] = $2;
$$ = $1;
}
@ -602,7 +608,7 @@ proc_stmts : proc_stmts proc_stmt
new->stmts_alloc = 64;
new->stmts_used = 1;
new->stmts = malloc(sizeof(PLpgSQL_stmt *) * new->stmts_alloc);
new->stmts[0] = (struct PLpgSQL_stmt *)$1;
new->stmts[0] = $1;
$$ = new;
@ -832,7 +838,7 @@ stmt_else :
new->stmts_alloc = 64;
new->stmts_used = 1;
new->stmts = malloc(sizeof(PLpgSQL_stmt *) * new->stmts_alloc);
new->stmts[0] = (struct PLpgSQL_stmt *)new_if;
new->stmts[0] = (PLpgSQL_stmt *) new_if;
$$ = new;
@ -1524,6 +1530,54 @@ execsql_start : T_WORD
{ $$ = strdup(yytext); }
;
exception_sect :
{ $$ = NULL; }
| K_EXCEPTION proc_exceptions
{ $$ = $2; }
;
proc_exceptions : proc_exceptions proc_exception
{
if ($1->exceptions_used == $1->exceptions_alloc)
{
$1->exceptions_alloc *= 2;
$1->exceptions = realloc($1->exceptions, sizeof(PLpgSQL_exception *) * $1->exceptions_alloc);
}
$1->exceptions[$1->exceptions_used++] = $2;
$$ = $1;
}
| proc_exception
{
PLpgSQL_exceptions *new;
new = malloc(sizeof(PLpgSQL_exceptions));
memset(new, 0, sizeof(PLpgSQL_exceptions));
new->exceptions_alloc = 64;
new->exceptions_used = 1;
new->exceptions = malloc(sizeof(PLpgSQL_exception *) * new->exceptions_alloc);
new->exceptions[0] = $1;
$$ = new;
}
;
proc_exception : K_WHEN lno opt_lblname K_THEN proc_sect
{
PLpgSQL_exception *new;
new = malloc(sizeof(PLpgSQL_exception));
memset(new, 0, sizeof(PLpgSQL_exception));
new->lineno = $2;
new->label = $3;
new->action = $5;
$$ = new;
}
;
expr_until_semi :
{ $$ = plpgsql_read_expression(';', ";"); }
;

View File

@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.108 2004/07/31 00:45:46 tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.109 2004/07/31 07:39:20 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@ -56,6 +56,16 @@
static const char *const raise_skip_msg = "RAISE";
typedef struct {
const char *label;
int sqlerrstate;
} ExceptionLabelMap;
static const ExceptionLabelMap exception_label_map[] = {
#include "plerrcodes.h"
{ NULL, 0 }
};
/*
* All plpgsql function executions within a single transaction share
* the same executor EState for evaluating "simple" expressions. Each
@ -784,6 +794,32 @@ copy_rec(PLpgSQL_rec * rec)
}
static bool
exception_matches_label(ErrorData *edata, const char *label)
{
int i;
/*
* OTHERS matches everything *except* query-canceled;
* if you're foolish enough, you can match that explicitly.
*/
if (pg_strcasecmp(label, "OTHERS") == 0)
{
if (edata->sqlerrcode == ERRCODE_QUERY_CANCELED)
return false;
else
return true;
}
for (i = 0; exception_label_map[i].label != NULL; i++)
{
if (pg_strcasecmp(label, exception_label_map[i].label) == 0)
return (edata->sqlerrcode == exception_label_map[i].sqlerrstate);
}
/* Should we raise an error if label is unrecognized?? */
return false;
}
/* ----------
* exec_stmt_block Execute a block of statements
* ----------
@ -791,7 +827,7 @@ copy_rec(PLpgSQL_rec * rec)
static int
exec_stmt_block(PLpgSQL_execstate * estate, PLpgSQL_stmt_block * block)
{
int rc;
volatile int rc = -1;
int i;
int n;
@ -859,13 +895,86 @@ exec_stmt_block(PLpgSQL_execstate * estate, PLpgSQL_stmt_block * block)
elog(ERROR, "unrecognized dtype: %d",
estate->datums[n]->dtype);
}
}
/*
* Execute the statements in the block's body
*/
rc = exec_stmts(estate, block->body);
if (block->exceptions)
{
/*
* Execute the statements in the block's body inside a sub-transaction
*/
MemoryContext oldcontext = CurrentMemoryContext;
volatile bool caught = false;
/*
* Start a subtransaction, and re-connect to SPI within it
*/
SPI_push();
BeginInternalSubTransaction(NULL);
/* Want to run statements inside function's memory context */
MemoryContextSwitchTo(oldcontext);
if (SPI_connect() != SPI_OK_CONNECT)
elog(ERROR, "SPI_connect failed");
PG_TRY();
{
rc = exec_stmts(estate, block->body);
}
PG_CATCH();
{
ErrorData *edata;
PLpgSQL_exceptions *exceptions;
int j;
/* Save error info */
MemoryContextSwitchTo(oldcontext);
edata = CopyErrorData();
FlushErrorState();
/* Abort the inner transaction (and inner SPI connection) */
RollbackAndReleaseCurrentSubTransaction();
MemoryContextSwitchTo(oldcontext);
SPI_pop();
/* Look for a matching exception handler */
exceptions = block->exceptions;
for (j = 0; j < exceptions->exceptions_used; j++)
{
PLpgSQL_exception *exception = exceptions->exceptions[j];
if (exception_matches_label(edata, exception->label))
{
rc = exec_stmts(estate, exception->action);
break;
}
}
/* If no match found, re-throw the error */
if (j >= exceptions->exceptions_used)
ReThrowError(edata);
else
FreeErrorData(edata);
caught = true;
}
PG_END_TRY();
/* Commit the inner transaction, return to outer xact context */
if (!caught)
{
if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish failed");
ReleaseCurrentSubTransaction();
MemoryContextSwitchTo(oldcontext);
SPI_pop();
}
}
else
{
/*
* Just execute the statements in the block's body
*/
rc = exec_stmts(estate, block->body);
}
/*
* Handle the return code.
@ -909,7 +1018,7 @@ exec_stmts(PLpgSQL_execstate * estate, PLpgSQL_stmts * stmts)
for (i = 0; i < stmts->stmts_used; i++)
{
rc = exec_stmt(estate, (PLpgSQL_stmt *) (stmts->stmts[i]));
rc = exec_stmt(estate, stmts->stmts[i]);
if (rc != PLPGSQL_RC_OK)
return rc;
}
@ -1852,7 +1961,8 @@ exec_stmt_raise(PLpgSQL_execstate * estate, PLpgSQL_stmt_raise * stmt)
estate->err_text = raise_skip_msg; /* suppress traceback of raise */
ereport(stmt->elog_level,
(errmsg_internal("%s", plpgsql_dstring_get(&ds))));
((stmt->elog_level >= ERROR) ? errcode(ERRCODE_RAISE_EXCEPTION) : 0,
errmsg_internal("%s", plpgsql_dstring_get(&ds))));
estate->err_text = NULL; /* un-suppress... */

View File

@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.32 2004/02/21 00:34:53 tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.33 2004/07/31 07:39:20 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@ -583,6 +583,17 @@ dump_stmt(PLpgSQL_stmt * stmt)
}
}
static void
dump_stmts(PLpgSQL_stmts * stmts)
{
int i;
dump_indent += 2;
for (i = 0; i < stmts->stmts_used; i++)
dump_stmt(stmts->stmts[i]);
dump_indent -= 2;
}
static void
dump_block(PLpgSQL_stmt_block * block)
{
@ -597,10 +608,19 @@ dump_block(PLpgSQL_stmt_block * block)
dump_ind();
printf("BLOCK <<%s>>\n", name);
dump_indent += 2;
for (i = 0; i < block->body->stmts_used; i++)
dump_stmt((PLpgSQL_stmt *) (block->body->stmts[i]));
dump_indent -= 2;
dump_stmts(block->body);
if (block->exceptions)
{
for (i = 0; i < block->exceptions->exceptions_used; i++)
{
PLpgSQL_exception *exc = block->exceptions->exceptions[i];
dump_ind();
printf(" EXCEPTION WHEN %s THEN\n", exc->label);
dump_stmts(exc->action);
}
}
dump_ind();
printf(" END -- %s\n", name);
@ -618,25 +638,17 @@ dump_assign(PLpgSQL_stmt_assign * stmt)
static void
dump_if(PLpgSQL_stmt_if * stmt)
{
int i;
dump_ind();
printf("IF ");
dump_expr(stmt->cond);
printf(" THEN\n");
dump_indent += 2;
for (i = 0; i < stmt->true_body->stmts_used; i++)
dump_stmt((PLpgSQL_stmt *) (stmt->true_body->stmts[i]));
dump_indent -= 2;
dump_stmts(stmt->true_body);
dump_ind();
printf(" ELSE\n");
dump_indent += 2;
for (i = 0; i < stmt->false_body->stmts_used; i++)
dump_stmt((PLpgSQL_stmt *) (stmt->false_body->stmts[i]));
dump_indent -= 2;
dump_stmts(stmt->false_body);
dump_ind();
printf(" ENDIF\n");
@ -645,15 +657,10 @@ dump_if(PLpgSQL_stmt_if * stmt)
static void
dump_loop(PLpgSQL_stmt_loop * stmt)
{
int i;
dump_ind();
printf("LOOP\n");
dump_indent += 2;
for (i = 0; i < stmt->body->stmts_used; i++)
dump_stmt((PLpgSQL_stmt *) (stmt->body->stmts[i]));
dump_indent -= 2;
dump_stmts(stmt->body);
dump_ind();
printf(" ENDLOOP\n");
@ -662,17 +669,12 @@ dump_loop(PLpgSQL_stmt_loop * stmt)
static void
dump_while(PLpgSQL_stmt_while * stmt)
{
int i;
dump_ind();
printf("WHILE ");
dump_expr(stmt->cond);
printf("\n");
dump_indent += 2;
for (i = 0; i < stmt->body->stmts_used; i++)
dump_stmt((PLpgSQL_stmt *) (stmt->body->stmts[i]));
dump_indent -= 2;
dump_stmts(stmt->body);
dump_ind();
printf(" ENDWHILE\n");
@ -681,8 +683,6 @@ dump_while(PLpgSQL_stmt_while * stmt)
static void
dump_fori(PLpgSQL_stmt_fori * stmt)
{
int i;
dump_ind();
printf("FORI %s %s\n", stmt->var->refname, (stmt->reverse) ? "REVERSE" : "NORMAL");
@ -695,11 +695,10 @@ dump_fori(PLpgSQL_stmt_fori * stmt)
printf(" upper = ");
dump_expr(stmt->upper);
printf("\n");
for (i = 0; i < stmt->body->stmts_used; i++)
dump_stmt((PLpgSQL_stmt *) (stmt->body->stmts[i]));
dump_indent -= 2;
dump_stmts(stmt->body);
dump_ind();
printf(" ENDFORI\n");
}
@ -707,17 +706,12 @@ dump_fori(PLpgSQL_stmt_fori * stmt)
static void
dump_fors(PLpgSQL_stmt_fors * stmt)
{
int i;
dump_ind();
printf("FORS %s ", (stmt->rec != NULL) ? stmt->rec->refname : stmt->row->refname);
dump_expr(stmt->query);
printf("\n");
dump_indent += 2;
for (i = 0; i < stmt->body->stmts_used; i++)
dump_stmt((PLpgSQL_stmt *) (stmt->body->stmts[i]));
dump_indent -= 2;
dump_stmts(stmt->body);
dump_ind();
printf(" ENDFORS\n");
@ -891,17 +885,12 @@ dump_dynexecute(PLpgSQL_stmt_dynexecute * stmt)
static void
dump_dynfors(PLpgSQL_stmt_dynfors * stmt)
{
int i;
dump_ind();
printf("FORS %s EXECUTE ", (stmt->rec != NULL) ? stmt->rec->refname : stmt->row->refname);
dump_expr(stmt->query);
printf("\n");
dump_indent += 2;
for (i = 0; i < stmt->body->stmts_used; i++)
dump_stmt((PLpgSQL_stmt *) (stmt->body->stmts[i]));
dump_indent -= 2;
dump_stmts(stmt->body);
dump_ind();
printf(" ENDFORS\n");
@ -1051,4 +1040,5 @@ plpgsql_dumptree(PLpgSQL_function * func)
printf("%3d:", func->action->lineno);
dump_block(func->action);
printf("\nEnd of execution tree of function %s\n\n", func->fn_name);
fflush(stdout);
}

View File

@ -0,0 +1,203 @@
/*-------------------------------------------------------------------------
*
* plerrcodes.h
* PL/pgSQL error codes (mapping of exception labels to SQLSTATEs)
*
* Eventually this header file should be auto-generated from errcodes.h
* with some sort of sed hackery, but no time for that now. It's likely
* that an exact mapping will not be what's wanted anyhow ...
*
* Copyright (c) 2003, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plerrcodes.h,v 1.1 2004/07/31 07:39:20 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{ "SUCCESSFUL_COMPLETION", ERRCODE_SUCCESSFUL_COMPLETION },
{ "WARNING", ERRCODE_WARNING },
{ "WARNING_DYNAMIC_RESULT_SETS_RETURNED", ERRCODE_WARNING_DYNAMIC_RESULT_SETS_RETURNED },
{ "WARNING_IMPLICIT_ZERO_BIT_PADDING", ERRCODE_WARNING_IMPLICIT_ZERO_BIT_PADDING },
{ "WARNING_NULL_VALUE_ELIMINATED_IN_SET_FUNCTION", ERRCODE_WARNING_NULL_VALUE_ELIMINATED_IN_SET_FUNCTION },
{ "WARNING_PRIVILEGE_NOT_GRANTED", ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED },
{ "WARNING_PRIVILEGE_NOT_REVOKED", ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED },
{ "WARNING_STRING_DATA_RIGHT_TRUNCATION", ERRCODE_WARNING_STRING_DATA_RIGHT_TRUNCATION },
{ "WARNING_DEPRECATED_FEATURE", ERRCODE_WARNING_DEPRECATED_FEATURE },
{ "NO_DATA", ERRCODE_NO_DATA },
{ "NO_ADDITIONAL_DYNAMIC_RESULT_SETS_RETURNED", ERRCODE_NO_ADDITIONAL_DYNAMIC_RESULT_SETS_RETURNED },
{ "SQL_STATEMENT_NOT_YET_COMPLETE", ERRCODE_SQL_STATEMENT_NOT_YET_COMPLETE },
{ "CONNECTION_EXCEPTION", ERRCODE_CONNECTION_EXCEPTION },
{ "CONNECTION_DOES_NOT_EXIST", ERRCODE_CONNECTION_DOES_NOT_EXIST },
{ "CONNECTION_FAILURE", ERRCODE_CONNECTION_FAILURE },
{ "SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION", ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION },
{ "SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION", ERRCODE_SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION },
{ "TRANSACTION_RESOLUTION_UNKNOWN", ERRCODE_TRANSACTION_RESOLUTION_UNKNOWN },
{ "PROTOCOL_VIOLATION", ERRCODE_PROTOCOL_VIOLATION },
{ "TRIGGERED_ACTION_EXCEPTION", ERRCODE_TRIGGERED_ACTION_EXCEPTION },
{ "FEATURE_NOT_SUPPORTED", ERRCODE_FEATURE_NOT_SUPPORTED },
{ "INVALID_TRANSACTION_INITIATION", ERRCODE_INVALID_TRANSACTION_INITIATION },
{ "LOCATOR_EXCEPTION", ERRCODE_LOCATOR_EXCEPTION },
{ "L_E_INVALID_SPECIFICATION", ERRCODE_L_E_INVALID_SPECIFICATION },
{ "INVALID_GRANTOR", ERRCODE_INVALID_GRANTOR },
{ "INVALID_GRANT_OPERATION", ERRCODE_INVALID_GRANT_OPERATION },
{ "INVALID_ROLE_SPECIFICATION", ERRCODE_INVALID_ROLE_SPECIFICATION },
{ "CARDINALITY_VIOLATION", ERRCODE_CARDINALITY_VIOLATION },
{ "DATA_EXCEPTION", ERRCODE_DATA_EXCEPTION },
{ "ARRAY_ELEMENT_ERROR", ERRCODE_ARRAY_ELEMENT_ERROR },
{ "ARRAY_SUBSCRIPT_ERROR", ERRCODE_ARRAY_SUBSCRIPT_ERROR },
{ "CHARACTER_NOT_IN_REPERTOIRE", ERRCODE_CHARACTER_NOT_IN_REPERTOIRE },
{ "DATETIME_FIELD_OVERFLOW", ERRCODE_DATETIME_FIELD_OVERFLOW },
{ "DATETIME_VALUE_OUT_OF_RANGE", ERRCODE_DATETIME_VALUE_OUT_OF_RANGE },
{ "DIVISION_BY_ZERO", ERRCODE_DIVISION_BY_ZERO },
{ "ERROR_IN_ASSIGNMENT", ERRCODE_ERROR_IN_ASSIGNMENT },
{ "ESCAPE_CHARACTER_CONFLICT", ERRCODE_ESCAPE_CHARACTER_CONFLICT },
{ "INDICATOR_OVERFLOW", ERRCODE_INDICATOR_OVERFLOW },
{ "INTERVAL_FIELD_OVERFLOW", ERRCODE_INTERVAL_FIELD_OVERFLOW },
{ "INVALID_ARGUMENT_FOR_LOG", ERRCODE_INVALID_ARGUMENT_FOR_LOG },
{ "INVALID_ARGUMENT_FOR_POWER_FUNCTION", ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION },
{ "INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION", ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION },
{ "INVALID_CHARACTER_VALUE_FOR_CAST", ERRCODE_INVALID_CHARACTER_VALUE_FOR_CAST },
{ "INVALID_DATETIME_FORMAT", ERRCODE_INVALID_DATETIME_FORMAT },
{ "INVALID_ESCAPE_CHARACTER", ERRCODE_INVALID_ESCAPE_CHARACTER },
{ "INVALID_ESCAPE_OCTET", ERRCODE_INVALID_ESCAPE_OCTET },
{ "INVALID_ESCAPE_SEQUENCE", ERRCODE_INVALID_ESCAPE_SEQUENCE },
{ "INVALID_INDICATOR_PARAMETER_VALUE", ERRCODE_INVALID_INDICATOR_PARAMETER_VALUE },
{ "INVALID_LIMIT_VALUE", ERRCODE_INVALID_LIMIT_VALUE },
{ "INVALID_PARAMETER_VALUE", ERRCODE_INVALID_PARAMETER_VALUE },
{ "INVALID_REGULAR_EXPRESSION", ERRCODE_INVALID_REGULAR_EXPRESSION },
{ "INVALID_TIME_ZONE_DISPLACEMENT_VALUE", ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE },
{ "INVALID_USE_OF_ESCAPE_CHARACTER", ERRCODE_INVALID_USE_OF_ESCAPE_CHARACTER },
{ "MOST_SPECIFIC_TYPE_MISMATCH", ERRCODE_MOST_SPECIFIC_TYPE_MISMATCH },
{ "NULL_VALUE_NOT_ALLOWED", ERRCODE_NULL_VALUE_NOT_ALLOWED },
{ "NULL_VALUE_NO_INDICATOR_PARAMETER", ERRCODE_NULL_VALUE_NO_INDICATOR_PARAMETER },
{ "NUMERIC_VALUE_OUT_OF_RANGE", ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE },
{ "STRING_DATA_LENGTH_MISMATCH", ERRCODE_STRING_DATA_LENGTH_MISMATCH },
{ "STRING_DATA_RIGHT_TRUNCATION", ERRCODE_STRING_DATA_RIGHT_TRUNCATION },
{ "SUBSTRING_ERROR", ERRCODE_SUBSTRING_ERROR },
{ "TRIM_ERROR", ERRCODE_TRIM_ERROR },
{ "UNTERMINATED_C_STRING", ERRCODE_UNTERMINATED_C_STRING },
{ "ZERO_LENGTH_CHARACTER_STRING", ERRCODE_ZERO_LENGTH_CHARACTER_STRING },
{ "FLOATING_POINT_EXCEPTION", ERRCODE_FLOATING_POINT_EXCEPTION },
{ "INVALID_TEXT_REPRESENTATION", ERRCODE_INVALID_TEXT_REPRESENTATION },
{ "INVALID_BINARY_REPRESENTATION", ERRCODE_INVALID_BINARY_REPRESENTATION },
{ "BAD_COPY_FILE_FORMAT", ERRCODE_BAD_COPY_FILE_FORMAT },
{ "UNTRANSLATABLE_CHARACTER", ERRCODE_UNTRANSLATABLE_CHARACTER },
{ "INTEGRITY_CONSTRAINT_VIOLATION", ERRCODE_INTEGRITY_CONSTRAINT_VIOLATION },
{ "RESTRICT_VIOLATION", ERRCODE_RESTRICT_VIOLATION },
{ "NOT_NULL_VIOLATION", ERRCODE_NOT_NULL_VIOLATION },
{ "FOREIGN_KEY_VIOLATION", ERRCODE_FOREIGN_KEY_VIOLATION },
{ "UNIQUE_VIOLATION", ERRCODE_UNIQUE_VIOLATION },
{ "CHECK_VIOLATION", ERRCODE_CHECK_VIOLATION },
{ "INVALID_CURSOR_STATE", ERRCODE_INVALID_CURSOR_STATE },
{ "INVALID_TRANSACTION_STATE", ERRCODE_INVALID_TRANSACTION_STATE },
{ "ACTIVE_SQL_TRANSACTION", ERRCODE_ACTIVE_SQL_TRANSACTION },
{ "BRANCH_TRANSACTION_ALREADY_ACTIVE", ERRCODE_BRANCH_TRANSACTION_ALREADY_ACTIVE },
{ "HELD_CURSOR_REQUIRES_SAME_ISOLATION_LEVEL", ERRCODE_HELD_CURSOR_REQUIRES_SAME_ISOLATION_LEVEL },
{ "INAPPROPRIATE_ACCESS_MODE_FOR_BRANCH_TRANSACTION", ERRCODE_INAPPROPRIATE_ACCESS_MODE_FOR_BRANCH_TRANSACTION },
{ "INAPPROPRIATE_ISOLATION_LEVEL_FOR_BRANCH_TRANSACTION", ERRCODE_INAPPROPRIATE_ISOLATION_LEVEL_FOR_BRANCH_TRANSACTION },
{ "NO_ACTIVE_SQL_TRANSACTION_FOR_BRANCH_TRANSACTION", ERRCODE_NO_ACTIVE_SQL_TRANSACTION_FOR_BRANCH_TRANSACTION },
{ "READ_ONLY_SQL_TRANSACTION", ERRCODE_READ_ONLY_SQL_TRANSACTION },
{ "SCHEMA_AND_DATA_STATEMENT_MIXING_NOT_SUPPORTED", ERRCODE_SCHEMA_AND_DATA_STATEMENT_MIXING_NOT_SUPPORTED },
{ "NO_ACTIVE_SQL_TRANSACTION", ERRCODE_NO_ACTIVE_SQL_TRANSACTION },
{ "IN_FAILED_SQL_TRANSACTION", ERRCODE_IN_FAILED_SQL_TRANSACTION },
{ "INVALID_SQL_STATEMENT_NAME", ERRCODE_INVALID_SQL_STATEMENT_NAME },
{ "TRIGGERED_DATA_CHANGE_VIOLATION", ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION },
{ "INVALID_AUTHORIZATION_SPECIFICATION", ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION },
{ "DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST", ERRCODE_DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST },
{ "DEPENDENT_OBJECTS_STILL_EXIST", ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST },
{ "INVALID_TRANSACTION_TERMINATION", ERRCODE_INVALID_TRANSACTION_TERMINATION },
{ "SQL_ROUTINE_EXCEPTION", ERRCODE_SQL_ROUTINE_EXCEPTION },
{ "S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT", ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT },
{ "S_R_E_MODIFYING_SQL_DATA_NOT_PERMITTED", ERRCODE_S_R_E_MODIFYING_SQL_DATA_NOT_PERMITTED },
{ "S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED", ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED },
{ "S_R_E_READING_SQL_DATA_NOT_PERMITTED", ERRCODE_S_R_E_READING_SQL_DATA_NOT_PERMITTED },
{ "INVALID_CURSOR_NAME", ERRCODE_INVALID_CURSOR_NAME },
{ "EXTERNAL_ROUTINE_EXCEPTION", ERRCODE_EXTERNAL_ROUTINE_EXCEPTION },
{ "E_R_E_CONTAINING_SQL_NOT_PERMITTED", ERRCODE_E_R_E_CONTAINING_SQL_NOT_PERMITTED },
{ "E_R_E_MODIFYING_SQL_DATA_NOT_PERMITTED", ERRCODE_E_R_E_MODIFYING_SQL_DATA_NOT_PERMITTED },
{ "E_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED", ERRCODE_E_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED },
{ "E_R_E_READING_SQL_DATA_NOT_PERMITTED", ERRCODE_E_R_E_READING_SQL_DATA_NOT_PERMITTED },
{ "EXTERNAL_ROUTINE_INVOCATION_EXCEPTION", ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION },
{ "E_R_I_E_INVALID_SQLSTATE_RETURNED", ERRCODE_E_R_I_E_INVALID_SQLSTATE_RETURNED },
{ "E_R_I_E_NULL_VALUE_NOT_ALLOWED", ERRCODE_E_R_I_E_NULL_VALUE_NOT_ALLOWED },
{ "E_R_I_E_TRIGGER_PROTOCOL_VIOLATED", ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED },
{ "E_R_I_E_SRF_PROTOCOL_VIOLATED", ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED },
{ "SAVEPOINT_EXCEPTION", ERRCODE_SAVEPOINT_EXCEPTION },
{ "S_E_INVALID_SPECIFICATION", ERRCODE_S_E_INVALID_SPECIFICATION },
{ "INVALID_CATALOG_NAME", ERRCODE_INVALID_CATALOG_NAME },
{ "INVALID_SCHEMA_NAME", ERRCODE_INVALID_SCHEMA_NAME },
{ "TRANSACTION_ROLLBACK", ERRCODE_TRANSACTION_ROLLBACK },
{ "T_R_INTEGRITY_CONSTRAINT_VIOLATION", ERRCODE_T_R_INTEGRITY_CONSTRAINT_VIOLATION },
{ "T_R_SERIALIZATION_FAILURE", ERRCODE_T_R_SERIALIZATION_FAILURE },
{ "T_R_STATEMENT_COMPLETION_UNKNOWN", ERRCODE_T_R_STATEMENT_COMPLETION_UNKNOWN },
{ "T_R_DEADLOCK_DETECTED", ERRCODE_T_R_DEADLOCK_DETECTED },
{ "SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION", ERRCODE_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION },
{ "SYNTAX_ERROR", ERRCODE_SYNTAX_ERROR },
{ "INSUFFICIENT_PRIVILEGE", ERRCODE_INSUFFICIENT_PRIVILEGE },
{ "CANNOT_COERCE", ERRCODE_CANNOT_COERCE },
{ "GROUPING_ERROR", ERRCODE_GROUPING_ERROR },
{ "INVALID_FOREIGN_KEY", ERRCODE_INVALID_FOREIGN_KEY },
{ "INVALID_NAME", ERRCODE_INVALID_NAME },
{ "NAME_TOO_LONG", ERRCODE_NAME_TOO_LONG },
{ "RESERVED_NAME", ERRCODE_RESERVED_NAME },
{ "DATATYPE_MISMATCH", ERRCODE_DATATYPE_MISMATCH },
{ "INDETERMINATE_DATATYPE", ERRCODE_INDETERMINATE_DATATYPE },
{ "WRONG_OBJECT_TYPE", ERRCODE_WRONG_OBJECT_TYPE },
{ "UNDEFINED_COLUMN", ERRCODE_UNDEFINED_COLUMN },
{ "UNDEFINED_CURSOR", ERRCODE_UNDEFINED_CURSOR },
{ "UNDEFINED_DATABASE", ERRCODE_UNDEFINED_DATABASE },
{ "UNDEFINED_FUNCTION", ERRCODE_UNDEFINED_FUNCTION },
{ "UNDEFINED_PSTATEMENT", ERRCODE_UNDEFINED_PSTATEMENT },
{ "UNDEFINED_SCHEMA", ERRCODE_UNDEFINED_SCHEMA },
{ "UNDEFINED_TABLE", ERRCODE_UNDEFINED_TABLE },
{ "UNDEFINED_PARAMETER", ERRCODE_UNDEFINED_PARAMETER },
{ "UNDEFINED_OBJECT", ERRCODE_UNDEFINED_OBJECT },
{ "DUPLICATE_COLUMN", ERRCODE_DUPLICATE_COLUMN },
{ "DUPLICATE_CURSOR", ERRCODE_DUPLICATE_CURSOR },
{ "DUPLICATE_DATABASE", ERRCODE_DUPLICATE_DATABASE },
{ "DUPLICATE_FUNCTION", ERRCODE_DUPLICATE_FUNCTION },
{ "DUPLICATE_PSTATEMENT", ERRCODE_DUPLICATE_PSTATEMENT },
{ "DUPLICATE_SCHEMA", ERRCODE_DUPLICATE_SCHEMA },
{ "DUPLICATE_TABLE", ERRCODE_DUPLICATE_TABLE },
{ "DUPLICATE_ALIAS", ERRCODE_DUPLICATE_ALIAS },
{ "DUPLICATE_OBJECT", ERRCODE_DUPLICATE_OBJECT },
{ "AMBIGUOUS_COLUMN", ERRCODE_AMBIGUOUS_COLUMN },
{ "AMBIGUOUS_FUNCTION", ERRCODE_AMBIGUOUS_FUNCTION },
{ "AMBIGUOUS_PARAMETER", ERRCODE_AMBIGUOUS_PARAMETER },
{ "AMBIGUOUS_ALIAS", ERRCODE_AMBIGUOUS_ALIAS },
{ "INVALID_COLUMN_REFERENCE", ERRCODE_INVALID_COLUMN_REFERENCE },
{ "INVALID_COLUMN_DEFINITION", ERRCODE_INVALID_COLUMN_DEFINITION },
{ "INVALID_CURSOR_DEFINITION", ERRCODE_INVALID_CURSOR_DEFINITION },
{ "INVALID_DATABASE_DEFINITION", ERRCODE_INVALID_DATABASE_DEFINITION },
{ "INVALID_FUNCTION_DEFINITION", ERRCODE_INVALID_FUNCTION_DEFINITION },
{ "INVALID_PSTATEMENT_DEFINITION", ERRCODE_INVALID_PSTATEMENT_DEFINITION },
{ "INVALID_SCHEMA_DEFINITION", ERRCODE_INVALID_SCHEMA_DEFINITION },
{ "INVALID_TABLE_DEFINITION", ERRCODE_INVALID_TABLE_DEFINITION },
{ "INVALID_OBJECT_DEFINITION", ERRCODE_INVALID_OBJECT_DEFINITION },
{ "WITH_CHECK_OPTION_VIOLATION", ERRCODE_WITH_CHECK_OPTION_VIOLATION },
{ "INSUFFICIENT_RESOURCES", ERRCODE_INSUFFICIENT_RESOURCES },
{ "DISK_FULL", ERRCODE_DISK_FULL },
{ "OUT_OF_MEMORY", ERRCODE_OUT_OF_MEMORY },
{ "TOO_MANY_CONNECTIONS", ERRCODE_TOO_MANY_CONNECTIONS },
{ "PROGRAM_LIMIT_EXCEEDED", ERRCODE_PROGRAM_LIMIT_EXCEEDED },
{ "STATEMENT_TOO_COMPLEX", ERRCODE_STATEMENT_TOO_COMPLEX },
{ "TOO_MANY_COLUMNS", ERRCODE_TOO_MANY_COLUMNS },
{ "TOO_MANY_ARGUMENTS", ERRCODE_TOO_MANY_ARGUMENTS },
{ "OBJECT_NOT_IN_PREREQUISITE_STATE", ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE },
{ "OBJECT_IN_USE", ERRCODE_OBJECT_IN_USE },
{ "CANT_CHANGE_RUNTIME_PARAM", ERRCODE_CANT_CHANGE_RUNTIME_PARAM },
{ "OPERATOR_INTERVENTION", ERRCODE_OPERATOR_INTERVENTION },
{ "QUERY_CANCELED", ERRCODE_QUERY_CANCELED },
{ "ADMIN_SHUTDOWN", ERRCODE_ADMIN_SHUTDOWN },
{ "CRASH_SHUTDOWN", ERRCODE_CRASH_SHUTDOWN },
{ "CANNOT_CONNECT_NOW", ERRCODE_CANNOT_CONNECT_NOW },
{ "IO_ERROR", ERRCODE_IO_ERROR },
{ "UNDEFINED_FILE", ERRCODE_UNDEFINED_FILE },
{ "DUPLICATE_FILE", ERRCODE_DUPLICATE_FILE },
{ "CONFIG_FILE_ERROR", ERRCODE_CONFIG_FILE_ERROR },
{ "LOCK_FILE_EXISTS", ERRCODE_LOCK_FILE_EXISTS },
{ "PLPGSQL_ERROR", ERRCODE_PLPGSQL_ERROR },
{ "RAISE_EXCEPTION", ERRCODE_RAISE_EXCEPTION },
{ "INTERNAL_ERROR", ERRCODE_INTERNAL_ERROR },
{ "DATA_CORRUPTED", ERRCODE_DATA_CORRUPTED },
{ "INDEX_CORRUPTED", ERRCODE_INDEX_CORRUPTED },

View File

@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.47 2004/06/06 00:41:28 tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.48 2004/07/31 07:39:20 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@ -307,14 +307,6 @@ typedef struct PLpgSQL_ns
} PLpgSQL_ns;
typedef struct
{ /* List of execution nodes */
int stmts_alloc;
int stmts_used;
struct PLpgSQL_stmt **stmts;
} PLpgSQL_stmts;
typedef struct
{ /* Generic execution node */
int cmd_type;
@ -322,12 +314,37 @@ typedef struct
} PLpgSQL_stmt;
typedef struct
{ /* List of execution nodes */
int stmts_alloc; /* XXX this oughta just be a List ... */
int stmts_used;
PLpgSQL_stmt **stmts;
} PLpgSQL_stmts;
typedef struct
{ /* One EXCEPTION ... WHEN clause */
int lineno;
char *label;
PLpgSQL_stmts *action;
} PLpgSQL_exception;
typedef struct
{ /* List of WHEN clauses */
int exceptions_alloc; /* XXX this oughta just be a List ... */
int exceptions_used;
PLpgSQL_exception **exceptions;
} PLpgSQL_exceptions;
typedef struct
{ /* Block of statements */
int cmd_type;
int lineno;
char *label;
PLpgSQL_stmts *body;
PLpgSQL_exceptions *exceptions;
int n_initvars;
int *initvarnos;
} PLpgSQL_stmt_block;

View File

@ -1793,3 +1793,60 @@ SELECT * FROM perform_test;
(3 rows)
drop table perform_test;
--
-- Test error trapping
--
create function trap_zero_divide(int) returns int as $$
declare x int;
declare sx smallint;
begin
begin -- start a subtransaction
raise notice 'should see this';
x := 100 / $1;
raise notice 'should see this only if % <> 0', $1;
sx := $1;
raise notice 'should see this only if % fits in smallint', $1;
if $1 < 0 then
raise exception '% is less than zero', $1;
end if;
exception
when division_by_zero then
raise notice 'caught division_by_zero';
x := -1;
when NUMERIC_VALUE_OUT_OF_RANGE then
raise notice 'caught numeric_value_out_of_range';
x := -2;
end;
return x;
end$$ language plpgsql;
select trap_zero_divide(50);
NOTICE: should see this
NOTICE: should see this only if 50 <> 0
NOTICE: should see this only if 50 fits in smallint
trap_zero_divide
------------------
2
(1 row)
select trap_zero_divide(0);
NOTICE: should see this
NOTICE: caught division_by_zero
trap_zero_divide
------------------
-1
(1 row)
select trap_zero_divide(100000);
NOTICE: should see this
NOTICE: should see this only if 100000 <> 0
NOTICE: caught numeric_value_out_of_range
trap_zero_divide
------------------
-2
(1 row)
select trap_zero_divide(-100);
NOTICE: should see this
NOTICE: should see this only if -100 <> 0
NOTICE: should see this only if -100 fits in smallint
ERROR: -100 is less than zero

View File

@ -1608,3 +1608,36 @@ SELECT perform_test_func();
SELECT * FROM perform_test;
drop table perform_test;
--
-- Test error trapping
--
create function trap_zero_divide(int) returns int as $$
declare x int;
declare sx smallint;
begin
begin -- start a subtransaction
raise notice 'should see this';
x := 100 / $1;
raise notice 'should see this only if % <> 0', $1;
sx := $1;
raise notice 'should see this only if % fits in smallint', $1;
if $1 < 0 then
raise exception '% is less than zero', $1;
end if;
exception
when division_by_zero then
raise notice 'caught division_by_zero';
x := -1;
when NUMERIC_VALUE_OUT_OF_RANGE then
raise notice 'caught numeric_value_out_of_range';
x := -2;
end;
return x;
end$$ language plpgsql;
select trap_zero_divide(50);
select trap_zero_divide(0);
select trap_zero_divide(100000);
select trap_zero_divide(-100);