Hello This patch respect last Tom's Lane notes. Is based on first variante,
http://archives.postgresql.org/pgsql-committers/2005-05/msg00303.php SQLSTATE and SQLERRM are local variables blocks with EXCEPTION clause. I wonted respect a litlle bit Oracle behavior and therefore exist these variables on function scope too, allways "00000" and "Successful completation" (allways marked isconst). I am really not sure what is better, respect Oracle or not with visibility SQLSTATE and SQLERRM on function scope. Is true, using these variables outside EXCEPTION is nonsens, but on other hand all magic variables has function scope: FOUND, NEW, OLD, ... . SQLSTATE and SQLERRM are little bit similar. If are you in oportunity, please correct this patch. It's easy. Clean all parts with these on functions related procedures. Then is neccesery change documentation, add info about outside exceptions blocks is impossible use SQLSTATE and SQLERRM variables. (regress too) Regards Pavel Stehule 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 ------------ Regards Pavel Stehule
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 <literal>Tom Jones</> not <literal>Joe Jones</>. </para> + <para>You can use variables <literal>SQLSTATE</literal> and <literal>SQLERRM</literal> + for detection type and message of exception (expecially in <literal>EXCEPTION WHEN OTHERS</literal> + block. <literal>SQLSTATE</literal> contains five numbers exception's code, + <literal>SQLSTATE</literal> contains text of exception's message. Outside + block with <literal>EXCEPTION</literal> clause <literal>SQLSTATE</literal> + and <literal>SQLERRM</literal> are "00000" and "Successfull completation". + </para> + + <note> + <para>Values of <literal>SQLSTATE</literal> are not compatible with ORACLE + <literal>SQLSTATE</literal> values. + </para> + </note> + + <tip> <para> A block containing an <literal>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> stmt_dynexecute stmt_getdiag %type <stmt> stmt_open stmt_fetch stmt_close stmt_null ! %type <list> exception_sect proc_exceptions %type <exception> proc_exception %type <condition> proc_conditions --- 135,142 ---- %type <stmt> stmt_dynexecute stmt_getdiag %type <stmt> stmt_open stmt_fetch stmt_close stmt_null ! %type <exception_with_vars> exception_sect fict_vars_sect ! %type <list> proc_exceptions %type <exception> proc_exception %type <condition> 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();
---------------------------(end of broadcast)--------------------------- TIP 1: subscribe and unsubscribe commands go to [EMAIL PROTECTED]