diff -c -r --new-file pgsql.00/doc/src/sgml/plpgsql.sgml pgsql.01/doc/src/sgml/plpgsql.sgml *** pgsql.00/doc/src/sgml/plpgsql.sgml 2005-06-06 15:29:00.000000000 +0200 --- pgsql.01/doc/src/sgml/plpgsql.sgml 2005-06-08 00:32:14.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. These + variables exist only inside exception block and using them outside exception + block cause syntax error. + + + + Values of SQLSTATE are not compatible with ORACLE + SQLSTATE values. + + + + A block containing an EXCEPTION clause is significantly diff -c -r --new-file pgsql.00/src/pl/plpgsql/src/gram.y pgsql.01/src/pl/plpgsql/src/gram.y *** pgsql.00/src/pl/plpgsql/src/gram.y 2005-06-06 15:29:53.000000000 +0200 --- pgsql.01/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.00/src/pl/plpgsql/src/pl_comp.c pgsql.01/src/pl/plpgsql/src/pl_comp.c *** pgsql.00/src/pl/plpgsql/src/pl_comp.c 2005-06-06 15:29:53.000000000 +0200 --- pgsql.01/src/pl/plpgsql/src/pl_comp.c 2005-06-08 00:08:42.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,635 ---- 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 */ diff -c -r --new-file pgsql.00/src/pl/plpgsql/src/pl_exec.c pgsql.01/src/pl/plpgsql/src/pl_exec.c *** pgsql.00/src/pl/plpgsql/src/pl_exec.c 2005-06-06 15:29:53.000000000 +0200 --- pgsql.01/src/pl/plpgsql/src/pl_exec.c 2005-06-08 00:09:52.000000000 +0200 *************** *** 180,185 **** --- 180,186 ---- 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); /* ---------- *************** *** 272,278 **** } /* ! * Set the magic variable FOUND to false */ exec_set_found(&estate, false); --- 273,279 ---- } /* ! * Set the magic variable FOUND */ exec_set_found(&estate, false); *************** *** 747,752 **** --- 748,771 ---- 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 **** --- 875,889 ---- 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 **** --- 2033,2039 ---- estate->err_func = func; estate->err_stmt = NULL; estate->err_text = NULL; + } /* ---------- *************** *** 4282,4287 **** --- 4311,4339 ---- var->isnull = false; } + /* + * 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.00/src/pl/plpgsql/src/plpgsql.h pgsql.01/src/pl/plpgsql/src/plpgsql.h *** pgsql.00/src/pl/plpgsql/src/plpgsql.h 2005-06-06 15:29:53.000000000 +0200 --- pgsql.01/src/pl/plpgsql/src/plpgsql.h 2005-06-08 00:10:20.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,591 ---- int tg_nargs_varno; int ndatums; + PLpgSQL_datum **datums; PLpgSQL_stmt_block *action; } PLpgSQL_function; diff -c -r --new-file pgsql.00/src/test/regress/expected/plpgsql.out pgsql.01/src/test/regress/expected/plpgsql.out *** pgsql.00/src/test/regress/expected/plpgsql.out 2005-06-06 15:30:06.000000000 +0200 --- pgsql.01/src/test/regress/expected/plpgsql.out 2005-06-08 00:34:58.000000000 +0200 *************** *** 2380,2382 **** --- 2380,2442 ---- CONTEXT: PL/pgSQL function "missing_return_expr" drop function void_return_expr(); drop function missing_return_expr(); + -- + -- SQLSTATE and SQLERRM test + -- + -- Wrong SQLSTATE and SQLERRM are only in EXCEPTION block + 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; + end; + end; $$ language plpgsql; + ERROR: syntax error at or near "sqlstate" at character 79 + LINE 3: raise notice '% %', sqlstate, sqlerrm; + ^ + -- Wrong SQLSTATE and SQLERRM are only in EXCEPTION block + create function excpt_test() returns void as $$ + begin + begin + raise notice '% %', sqlstate, sqlerrm; + raise exception 'usr exception'; + exception when others then + raise notice 'caught usr exception % %', sqlstate, sqlerrm; + end; + end; $$ language plpgsql; + ERROR: syntax error at or near "sqlstate" at character 90 + LINE 4: raise notice '% %', sqlstate, sqlerrm; + ^ + -- + -- SQLSTATE and SQLERRM test + -- + create function excpt_test() returns void as $$ + begin + 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; + end; $$ language plpgsql; + CREATE FUNCTION + select excpt_test(); + NOTICE: caught usr exception P0001 usr exception + NOTICE: P0001 usr exception + NOTICE: caught usr exception 22012 division by zero + NOTICE: P0001 usr exception + excpt_test + ------------ + + (1 row) + + drop function excpt_test(); + DROP FUNCTION diff -c -r --new-file pgsql.00/src/test/regress/sql/plpgsql.sql pgsql.01/src/test/regress/sql/plpgsql.sql *** pgsql.00/src/test/regress/sql/plpgsql.sql 2005-06-06 15:30:03.000000000 +0200 --- pgsql.01/src/test/regress/sql/plpgsql.sql 2005-06-08 00:24:02.000000000 +0200 *************** *** 2018,2020 **** --- 2018,2068 ---- drop function void_return_expr(); drop function missing_return_expr(); + + -- + -- SQLSTATE and SQLERRM test + -- + -- Wrong SQLSTATE and SQLERRM are only in EXCEPTION block + 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; + end; + end; $$ language plpgsql; + + -- Wrong SQLSTATE and SQLERRM are only in EXCEPTION block + create function excpt_test() returns void as $$ + begin + begin + raise notice '% %', sqlstate, sqlerrm; + raise exception 'usr exception'; + exception when others then + raise notice 'caught usr exception % %', sqlstate, sqlerrm; + end; + end; $$ language plpgsql; + + -- + -- SQLSTATE and SQLERRM test + -- + create function excpt_test() returns void as $$ + begin + 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; + end; $$ language plpgsql; + + select excpt_test(); + + drop function excpt_test();