diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml index b30cc7e83f..7f3f84448d 100644 --- a/doc/src/sgml/plpgsql.sgml +++ b/doc/src/sgml/plpgsql.sgml @@ -1,5 +1,5 @@ @@ -1796,6 +1796,101 @@ END LOOP; rather than the simple syntax error one might expect to get. + + + + Trapping Errors + + + By default, any error occurring in a 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 BEGIN block with an + EXCEPTION clause. The syntax is an extension of the + normal syntax for a BEGIN block: + + + <<label>> + DECLARE + declarations +BEGIN + statements +EXCEPTION + WHEN condition THEN + handler_statements + WHEN condition THEN + handler_statements + ... + +END; + + + + + If no error occurs, this form of block simply executes all the + statements, and then control passes + to the next statement after END. But if an error + occurs within the statements, further + processing of the statements is + abandoned, and control passes to the EXCEPTION list. + The list is searched for the first condition + matching the error that occurred. If a match is found, the + corresponding handler_statements are + executed, and then control passes to the next statement after + END. If no match is found, the error propagates out + as though the EXCEPTION clause were not there at all: + the error can be caught by an enclosing block with + EXCEPTION, or if there is none it aborts processing + of the function. The special condition name OTHERS + matches every error type except QUERY_CANCELED. + (It is possible, but usually not a good idea, to trap + QUERY_CANCELED by name.) + + + + If a new error occurs within the selected + handler_statements, it cannot be caught + by this EXCEPTION clause, but is propagated out. + A surrounding EXCEPTION clause could catch it. + + + + When an error is caught by an EXCEPTION clause, + the local variables of the 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: + + + 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; + + + When control reaches the assignment to y, it will + fail with a division_by_zero error. This will be caught by + the EXCEPTION clause. The value returned in the + RETURN statement will be the incremented value of + x, but the effects of the UPDATE command will + have been rolled back. The INSERT command is not rolled + back, however, so the end result is that the database contains + Tom Jones not Joe Jones. + + + + + A block containing an EXCEPTION clause is significantly + more expensive to enter and exit than a block without one. Therefore, + don't use EXCEPTION without need. + + @@ -2120,11 +2215,11 @@ RAISE level '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; diff --git a/src/include/access/xact.h b/src/include/access/xact.h index 7bf92b0153..532dcf51b0 100644 --- a/src/include/access/xact.h +++ b/src/include/access/xact.h @@ -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); diff --git a/src/include/utils/errcodes.h b/src/include/utils/errcodes.h index f3ac7f6c0b..a695b90a2e 100644 --- a/src/include/utils/errcodes.h +++ b/src/include/utils/errcodes.h @@ -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') diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y index 9b70d944ab..628c81b570 100644 --- a/src/pl/plpgsql/src/gram.y +++ b/src/pl/plpgsql/src/gram.y @@ -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_dynexecute stmt_getdiag %type stmt_open stmt_fetch stmt_close +%type exception_sect proc_exceptions +%type proc_exception + %type raise_params %type raise_level raise_param %type 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(';', ";"); } ; diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index d6f8c6835c..d8befe4d52 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -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... */ diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c index f49a2ac500..b464906752 100644 --- a/src/pl/plpgsql/src/pl_funcs.c +++ b/src/pl/plpgsql/src/pl_funcs.c @@ -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); } diff --git a/src/pl/plpgsql/src/plerrcodes.h b/src/pl/plpgsql/src/plerrcodes.h new file mode 100644 index 0000000000..4e76bde673 --- /dev/null +++ b/src/pl/plpgsql/src/plerrcodes.h @@ -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 }, diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index 92ece7da29..28868cf731 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -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; diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out index 694165e506..6907905cc5 100644 --- a/src/test/regress/expected/plpgsql.out +++ b/src/test/regress/expected/plpgsql.out @@ -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 diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql index 9cb7f7c940..48a78196a8 100644 --- a/src/test/regress/sql/plpgsql.sql +++ b/src/test/regress/sql/plpgsql.sql @@ -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);