diff -c -r --new-file pgsql.old/doc/src/sgml/plpgsql.sgml pgsql/doc/src/sgml/plpgsql.sgml *** pgsql.old/doc/src/sgml/plpgsql.sgml 2005-06-06 15:29:00.000000000 +0200 --- pgsql/doc/src/sgml/plpgsql.sgml 2005-06-06 20:46:00.000000000 +0200 *************** *** 2096,2101 **** --- 2096,2116 ---- contains Tom Jones not Joe Jones. + You can use variables SQLSTATE and SQLERRM + for detection type and message of exception (expecially in EXCEPTION WHEN OTHERS + block. SQLSTATE contains five numbers exception's code, + SQLSTATE contains text of exception's message. Outside + block with EXCEPTION clause SQLSTATE + and SQLERRM are "00000" and "Successfull completation". + + + + Values of SQLSTATE are not compatible with ORACLE + SQLSTATE values. + + + + A block containing an EXCEPTION clause is significantly diff -c -r --new-file pgsql.old/src/pl/plpgsql/src/gram.y pgsql/src/pl/plpgsql/src/gram.y *** pgsql.old/src/pl/plpgsql/src/gram.y 2005-06-06 15:29:53.000000000 +0200 --- pgsql/src/pl/plpgsql/src/gram.y 2005-06-06 20:30:30.000000000 +0200 *************** *** 80,85 **** --- 80,91 ---- int n_initvars; int *initvarnos; } declhdr; + struct + { + int sqlstate_varno; + int sqlerrm_varno; + List *proc_exceptions; + } exception_with_vars; List *list; PLpgSQL_type *dtype; PLpgSQL_datum *scalar; /* a VAR, RECFIELD, or TRIGARG */ *************** *** 129,135 **** %type stmt_dynexecute stmt_getdiag %type stmt_open stmt_fetch stmt_close stmt_null ! %type exception_sect proc_exceptions %type proc_exception %type proc_conditions --- 135,142 ---- %type stmt_dynexecute stmt_getdiag %type stmt_open stmt_fetch stmt_close stmt_null ! %type exception_sect fict_vars_sect ! %type proc_exceptions %type proc_exception %type proc_conditions *************** *** 256,262 **** new->n_initvars = $1.n_initvars; new->initvarnos = $1.initvarnos; new->body = $4; ! new->exceptions = $5; plpgsql_ns_pop(); --- 263,272 ---- new->n_initvars = $1.n_initvars; new->initvarnos = $1.initvarnos; new->body = $4; ! ! new->exceptions = $5.proc_exceptions; ! new->sqlstate_varno = $5.sqlstate_varno; ! new->sqlerrm_varno = $5.sqlerrm_varno; plpgsql_ns_pop(); *************** *** 1453,1463 **** ; exception_sect : ! { $$ = NIL; } ! | K_EXCEPTION proc_exceptions ! { $$ = $2; } ; proc_exceptions : proc_exceptions proc_exception { $$ = lappend($1, $2); --- 1463,1496 ---- ; exception_sect : ! { $$.proc_exceptions = NIL; } ! | K_EXCEPTION fict_vars_sect proc_exceptions ! { ! $$.proc_exceptions = $3; ! $$.sqlstate_varno = $2.sqlstate_varno; ! $$.sqlerrm_varno = $2.sqlerrm_varno; ! } ; + fict_vars_sect : + { + PLpgSQL_variable *var; + plpgsql_ns_setlocal(false); + + var = plpgsql_build_variable("sqlstate", 0, + plpgsql_build_datatype(TEXTOID, -1), true); + ((PLpgSQL_var *) var)->isconst = true; + $$.sqlstate_varno = var->dno; + + var = plpgsql_build_variable("sqlerrm", 0, + plpgsql_build_datatype(TEXTOID, -1), true); + ((PLpgSQL_var *) var)->isconst = true; + $$.sqlerrm_varno = var->dno; + } + ; + + + proc_exceptions : proc_exceptions proc_exception { $$ = lappend($1, $2); diff -c -r --new-file pgsql.old/src/pl/plpgsql/src/pl_comp.c pgsql/src/pl/plpgsql/src/pl_comp.c *** pgsql.old/src/pl/plpgsql/src/pl_comp.c 2005-06-06 15:29:53.000000000 +0200 --- pgsql/src/pl/plpgsql/src/pl_comp.c 2005-06-06 19:34:17.000000000 +0200 *************** *** 622,634 **** function->fn_readonly = (procStruct->provolatile != PROVOLATILE_VOLATILE); /* ! * Create the magic FOUND variable. */ var = plpgsql_build_variable("found", 0, plpgsql_build_datatype(BOOLOID, -1), true); function->found_varno = var->dno; /* * Forget about the above created variables */ --- 622,646 ---- function->fn_readonly = (procStruct->provolatile != PROVOLATILE_VOLATILE); /* ! * Create the magic FOUND variable and SQLSTATE and SQLERRM vars */ var = plpgsql_build_variable("found", 0, plpgsql_build_datatype(BOOLOID, -1), true); function->found_varno = var->dno; + var = plpgsql_build_variable("sqlstate", 0, + plpgsql_build_datatype(TEXTOID, -1), + true); + ((PLpgSQL_var *) var)->isconst = true; + function->sqlstate_varno = var->dno; + var = plpgsql_build_variable("sqlerrm", 0, + plpgsql_build_datatype(TEXTOID, -1), + true); + ((PLpgSQL_var *) var)->isconst = true; + function->sqlerrm_varno = var->dno; + + /* * Forget about the above created variables */ diff -c -r --new-file pgsql.old/src/pl/plpgsql/src/pl_exec.c pgsql/src/pl/plpgsql/src/pl_exec.c *** pgsql.old/src/pl/plpgsql/src/pl_exec.c 2005-06-06 15:29:53.000000000 +0200 --- pgsql/src/pl/plpgsql/src/pl_exec.c 2005-06-06 20:35:33.000000000 +0200 *************** *** 180,185 **** --- 180,189 ---- static void exec_init_tuple_store(PLpgSQL_execstate *estate); static bool compatible_tupdesc(TupleDesc td1, TupleDesc td2); static void exec_set_found(PLpgSQL_execstate *estate, bool state); + static char *unpack_sql_state(int ssval); + static void exec_reset_sqlstate(PLpgSQL_execstate *estate, int sqlstate_varno, + int sqlerrm_varno); + /* ---------- *************** *** 272,281 **** } /* ! * Set the magic variable FOUND to false */ exec_set_found(&estate, false); ! /* * Now call the toplevel block of statements */ --- 276,285 ---- } /* ! * Set the magic variable FOUND to false SQLSTATE 0000 and SQLERRM Successfull ... */ exec_set_found(&estate, false); ! exec_reset_sqlstate(&estate,func->sqlstate_varno, func->sqlerrm_varno); /* * Now call the toplevel block of statements */ *************** *** 536,544 **** } /* ! * Set the magic variable FOUND to false */ exec_set_found(&estate, false); /* * Now call the toplevel block of statements --- 540,549 ---- } /* ! * Set the magic variable FOUND to false, SQLSTATE 0000 and SQLERRM Successfull ... */ exec_set_found(&estate, false); + exec_reset_sqlstate(&estate,func->sqlstate_varno, func->sqlerrm_varno); /* * Now call the toplevel block of statements *************** *** 747,752 **** --- 752,775 ---- int i; int n; + /* setup SQLSTATE and SQLERRM really values visible only in exception blok */ + + PLpgSQL_var *var; + + if (block->sqlstate_varno && block->sqlerrm_varno) + { + 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("Successful completion")); + } + + /* * First initialize all variables declared in this block */ *************** *** 856,861 **** --- 879,893 ---- MemoryContextSwitchTo(oldcontext); CurrentResourceOwner = oldowner; + + var = (PLpgSQL_var *) (estate->datums[block->sqlstate_varno]); + pfree((void *) (var->value)); + var->value = DirectFunctionCall1(textin, CStringGetDatum(unpack_sql_state(edata->sqlerrcode))); + + var = (PLpgSQL_var *) (estate->datums[block->sqlerrm_varno]); + pfree((void *) (var->value)); + var->value = DirectFunctionCall1(textin, CStringGetDatum(edata->message)); + /* * If AtEOSubXact_SPI() popped any SPI context of the subxact, * it will have left us in a disconnected state. We need this *************** *** 2005,2010 **** --- 2037,2043 ---- estate->err_func = func; estate->err_stmt = NULL; estate->err_text = NULL; + } /* ---------- *************** *** 4282,4287 **** --- 4315,4361 ---- var->isnull = false; } + static void + exec_reset_sqlstate(PLpgSQL_execstate *estate, int sqlstate_varno, int sqlerrm_varno) + { + /* setup SQLSTATE and SQLERRM */ + PLpgSQL_var *var; + + var = (PLpgSQL_var *) (estate->datums[sqlstate_varno]); + var->isnull = false; + var->freeval = true; + var->value = DirectFunctionCall1(textin, CStringGetDatum("00000")); + + var = (PLpgSQL_var *) (estate->datums[sqlerrm_varno]); + var->isnull = false; + var->freeval = true; + var->value = DirectFunctionCall1(textin, CStringGetDatum("Successful completion")); + + } + + /* + * 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; + } + + + /* * plpgsql_xact_cb --- post-transaction-commit-or-abort cleanup * diff -c -r --new-file pgsql.old/src/pl/plpgsql/src/plpgsql.h pgsql/src/pl/plpgsql/src/plpgsql.h *** pgsql.old/src/pl/plpgsql/src/plpgsql.h 2005-06-06 15:29:53.000000000 +0200 --- pgsql/src/pl/plpgsql/src/plpgsql.h 2005-06-06 20:20:43.000000000 +0200 *************** *** 339,344 **** --- 339,346 ---- List *exceptions; /* List of WHEN clauses */ int n_initvars; int *initvarnos; + int sqlstate_varno; + int sqlerrm_varno; } PLpgSQL_stmt_block; *************** *** 583,588 **** --- 585,593 ---- int tg_nargs_varno; int ndatums; + int sqlstate_varno; + int sqlerrm_varno; + PLpgSQL_datum **datums; PLpgSQL_stmt_block *action; } PLpgSQL_function; diff -c -r --new-file pgsql.old/src/test/regress/expected/plpgsql.out pgsql/src/test/regress/expected/plpgsql.out *** pgsql.old/src/test/regress/expected/plpgsql.out 2005-06-06 15:30:06.000000000 +0200 --- pgsql/src/test/regress/expected/plpgsql.out 2005-06-06 20:48:57.000000000 +0200 *************** *** 2380,2382 **** --- 2380,2417 ---- CONTEXT: PL/pgSQL function "missing_return_expr" drop function void_return_expr(); drop function missing_return_expr(); + -- + -- SQLSTATE and SQLERRM test + -- + create function excpt_test() returns void as $$ + begin + raise notice '% %', sqlstate, sqlerrm; + begin + raise exception 'usr exception'; + exception when others then + raise notice 'caught usr exception % %', sqlstate, sqlerrm; + begin + raise notice '% %', sqlstate, sqlerrm; + perform 10/0; + exception when others then + raise notice 'caught usr exception % %', sqlstate, sqlerrm; + end; + raise notice '% %', sqlstate, sqlerrm; + end; + raise notice '% %', sqlstate, sqlerrm; + end; $$ language plpgsql; + CREATE FUNCTION + select excpt_test(); + NOTICE: 00000 Successful completion + NOTICE: caught usr exception P0001 usr exception + NOTICE: P0001 usr exception + NOTICE: caught usr exception 22012 division by zero + NOTICE: P0001 usr exception + NOTICE: 00000 Successful completion + excpt_test + ------------ + + (1 row) + + drop function excpt_test(); + DROP FUNCTION diff -c -r --new-file pgsql.old/src/test/regress/sql/plpgsql.sql pgsql/src/test/regress/sql/plpgsql.sql *** pgsql.old/src/test/regress/sql/plpgsql.sql 2005-06-06 15:30:03.000000000 +0200 --- pgsql/src/test/regress/sql/plpgsql.sql 2005-06-06 20:41:53.000000000 +0200 *************** *** 2018,2020 **** --- 2018,2045 ---- drop function void_return_expr(); drop function missing_return_expr(); + + -- + -- SQLSTATE and SQLERRM test + -- + create function excpt_test() returns void as $$ + begin + raise notice '% %', sqlstate, sqlerrm; + begin + raise exception 'usr exception'; + exception when others then + raise notice 'caught usr exception % %', sqlstate, sqlerrm; + begin + raise notice '% %', sqlstate, sqlerrm; + perform 10/0; + exception when others then + raise notice 'caught usr exception % %', sqlstate, sqlerrm; + end; + raise notice '% %', sqlstate, sqlerrm; + end; + raise notice '% %', sqlstate, sqlerrm; + end; $$ language plpgsql; + + select excpt_test(); + + drop function excpt_test();