*** ./doc/src/sgml/plpgsql.sgml.orig 2011-07-15 07:53:03.069787671 +0200
--- ./doc/src/sgml/plpgsql.sgml 2011-07-15 08:36:00.504591377 +0200
***************
*** 1387,1393 ****
command, which has the form:
! GET DIAGNOSTICS variable = item , ... ;
This command allows retrieval of system status indicators. Each
--- 1387,1393 ----
command, which has the form:
! GET CURRENT DIAGNOSTICS variable = item , ... ;
This command allows retrieval of system status indicators. Each
***************
*** 1488,1493 ****
--- 1488,1580 ----
+
+ Obtaining the Exception Status
+
+
+ Inside an exception handler, one may retrieve detailed
+ information about the current exception using THE
+ GET STACKED DIAGNOSTICS command, which has the form:
+
+
+ GET STACKED DIAGNOSTICS variable = item , ... ;
+
+
+
+
+ This command allows retrieval of the exception's data. Each
+ item is a key word identifying a state
+ value to be assigned to the specified variable (which should be
+ of the right data type to receive it). The currently available
+ status items are:
+
+
+ Stacked diagnostics values
+
+
+
+ Name
+ Return type
+ Description
+
+
+
+
+ RETURNED_SQLSTATE
+ text
+ the SQLSTATE of the exception
+
+
+ MESSAGE_TEXT
+ text
+ the text of the exception's message
+
+
+ PG_EXCEPTION_DETAIL
+ text
+ the text of the exception's detail message
+
+
+ PG_EXCEPTION_HINT
+ text
+ the text of the exception's hint message
+
+
+ PG_EXCEPTION_CONTEXT
+ text
+ lines of text describing the call stack
+
+
+
+
+
+
+
+ If an exception does not contain a value for an item, an empty string
+ will be returned.
+
+
+
+ An example:
+
+ DECLARE
+ text_var1 text;
+ text_var2 text;
+ text_var3 text;
+ BEGIN
+ -- some processing which might cause an exception
+ ...
+ EXCEPTION WHEN OTHERS THEN
+ GET STACKED DIAGNOSTICS text_var1 = MESSAGE_TEXT,
+ text_var2 = PG_EXCEPTION_DETAIL,
+ text_var3 = PG_EXCEPTION_HINT;
+ END;
+
+
+
+
+
+
Doing Nothing At All
*** ./src/backend/utils/errcodes.txt.orig 2011-07-15 07:53:03.070787661 +0200
--- ./src/backend/utils/errcodes.txt 2011-07-15 08:01:04.522609180 +0200
***************
*** 132,137 ****
--- 132,140 ----
0P000 E ERRCODE_INVALID_ROLE_SPECIFICATION invalid_role_specification
+ Section: Class 0Z - Diagnostics Exception
+ 0Z002 E ERRCODE_STACKED_DIAGNOSTICS_ACCESSED_WITHOUT_ACTIVE_HANDLER stacked_diagnostics_accessed_without_active_handler
+
Section: Class 20 - Case Not Found
20000 E ERRCODE_CASE_NOT_FOUND case_not_found
*** ./src/pl/plpgsql/src/gram.y.orig 2011-07-15 07:53:03.071787651 +0200
--- ./src/pl/plpgsql/src/gram.y 2011-07-15 09:29:27.959407772 +0200
***************
*** 206,211 ****
--- 206,212 ----
%type getdiag_list
%type getdiag_list_item
%type getdiag_item getdiag_target
+ %type getdiag_opt
%type opt_scrollable
%type opt_fetch_direction
***************
*** 250,256 ****
--- 251,259 ----
%token K_CLOSE
%token K_COLLATE
%token K_CONSTANT
+ %token K_CONTEXT
%token K_CONTINUE
+ %token K_CURRENT
%token K_CURSOR
%token K_DEBUG
%token K_DECLARE
***************
*** 263,268 ****
--- 266,274 ----
%token K_END
%token K_ERRCODE
%token K_ERROR
+ %token K_EXCEPTION_CONTEXT
+ %token K_EXCEPTION_DETAIL
+ %token K_EXCEPTION_HINT
%token K_EXCEPTION
%token K_EXECUTE
%token K_EXIT
***************
*** 284,289 ****
--- 290,296 ----
%token K_LOG
%token K_LOOP
%token K_MESSAGE
+ %token K_MESSAGE_TEXT
%token K_MOVE
%token K_NEXT
%token K_NO
***************
*** 300,311 ****
--- 307,320 ----
%token K_RELATIVE
%token K_RESULT_OID
%token K_RETURN
+ %token K_RETURNED_SQLSTATE
%token K_REVERSE
%token K_ROWTYPE
%token K_ROW_COUNT
%token K_SCROLL
%token K_SLICE
%token K_SQLSTATE
+ %token K_STACKED
%token K_STRICT
%token K_THEN
%token K_TO
***************
*** 832,845 ****
}
;
! stmt_getdiag : K_GET K_DIAGNOSTICS getdiag_list ';'
{
PLpgSQL_stmt_getdiag *new;
new = palloc0(sizeof(PLpgSQL_stmt_getdiag));
new->cmd_type = PLPGSQL_STMT_GETDIAG;
new->lineno = plpgsql_location_to_lineno(@1);
! new->diag_items = $3;
$$ = (PLpgSQL_stmt *)new;
}
--- 841,898 ----
}
;
! stmt_getdiag : K_GET getdiag_opt K_DIAGNOSTICS getdiag_list ';'
{
PLpgSQL_stmt_getdiag *new;
+ ListCell *lc;
new = palloc0(sizeof(PLpgSQL_stmt_getdiag));
new->cmd_type = PLPGSQL_STMT_GETDIAG;
new->lineno = plpgsql_location_to_lineno(@1);
! new->is_stacked = $2;
! new->diag_items = $4;
!
! /*
! * stacked diagnostics statements allows only access
! * to exception data fields. whereas current diagnostics
! * statement disallow access to exception data fields.
! */
! foreach(lc, new->diag_items)
! {
! PLpgSQL_diag_item *ditem = (PLpgSQL_diag_item *) lfirst(lc);
!
! if (new->is_stacked)
! {
! switch (ditem->kind)
! {
! /* these fields are disallowed in stacked diagnostics statement */
! case PLPGSQL_GETDIAG_ROW_COUNT:
! case PLPGSQL_GETDIAG_RESULT_OID:
! ereport(ERROR,
! (errcode(ERRCODE_SYNTAX_ERROR),
! errmsg("diagnostics value \"%s\" is not allowed in current diagnostics statement",
! plpgsql_getdiag_kindname(ditem->kind)),
! parser_errposition(@1)));
! }
! }
! else
! {
! switch (ditem->kind)
! {
! /* these fields are disallowed in current diagnostics statement */
! case PLPGSQL_GETDIAG_ERROR_CONTEXT:
! case PLPGSQL_GETDIAG_ERROR_DETAIL:
! case PLPGSQL_GETDIAG_ERROR_HINT:
! case PLPGSQL_GETDIAG_RETURNED_SQLSTATE:
! case PLPGSQL_GETDIAG_MESSAGE_TEXT:
! ereport(ERROR,
! (errcode(ERRCODE_SYNTAX_ERROR),
! errmsg("diagnostics value \"%s\" is not allowed in stacked diagnostics statement",
! plpgsql_getdiag_kindname(ditem->kind)),
! parser_errposition(@1)));
! }
! }
! }
$$ = (PLpgSQL_stmt *)new;
}
***************
*** 867,872 ****
--- 920,939 ----
}
;
+ getdiag_opt :
+ {
+ $$ = false;
+ }
+ | K_CURRENT
+ {
+ $$ = false;
+ }
+ | K_STACKED
+ {
+ $$ = true;
+ }
+ ;
+
getdiag_item :
{
int tok = yylex();
***************
*** 877,882 ****
--- 944,964 ----
else if (tok_is_keyword(tok, &yylval,
K_RESULT_OID, "result_oid"))
$$ = PLPGSQL_GETDIAG_RESULT_OID;
+ else if (tok_is_keyword(tok, &yylval,
+ K_EXCEPTION_DETAIL, "pg_exception_detail"))
+ $$ = PLPGSQL_GETDIAG_ERROR_DETAIL;
+ else if (tok_is_keyword(tok, &yylval,
+ K_EXCEPTION_HINT, "pg_exception_hint"))
+ $$ = PLPGSQL_GETDIAG_ERROR_HINT;
+ else if (tok_is_keyword(tok, &yylval,
+ K_EXCEPTION_CONTEXT, "pg_exception_context"))
+ $$ = PLPGSQL_GETDIAG_ERROR_CONTEXT;
+ else if (tok_is_keyword(tok, &yylval,
+ K_MESSAGE_TEXT, "message_text"))
+ $$ = PLPGSQL_GETDIAG_MESSAGE_TEXT;
+ else if (tok_is_keyword(tok, &yylval,
+ K_RETURNED_SQLSTATE, "returned_sqlstate"))
+ $$ = PLPGSQL_GETDIAG_RETURNED_SQLSTATE;
else
yyerror("unrecognized GET DIAGNOSTICS item");
}
***************
*** 2135,2146 ****
--- 2217,2232 ----
| K_ARRAY
| K_BACKWARD
| K_CONSTANT
+ | K_CURRENT
| K_CURSOR
| K_DEBUG
| K_DETAIL
| K_DUMP
| K_ERRCODE
| K_ERROR
+ | K_EXCEPTION_CONTEXT
+ | K_EXCEPTION_DETAIL
+ | K_EXCEPTION_HINT
| K_FIRST
| K_FORWARD
| K_HINT
***************
*** 2149,2154 ****
--- 2235,2241 ----
| K_LAST
| K_LOG
| K_MESSAGE
+ | K_MESSAGE_TEXT
| K_NEXT
| K_NO
| K_NOTICE
***************
*** 2157,2168 ****
--- 2244,2257 ----
| K_QUERY
| K_RELATIVE
| K_RESULT_OID
+ | K_RETURNED_SQLSTATE
| K_REVERSE
| K_ROW_COUNT
| K_ROWTYPE
| K_SCROLL
| K_SLICE
| K_SQLSTATE
+ | K_STACKED
| K_TYPE
| K_USE_COLUMN
| K_USE_VARIABLE
*** ./src/pl/plpgsql/src/pl_exec.c.orig 2011-07-15 07:53:03.073787632 +0200
--- ./src/pl/plpgsql/src/pl_exec.c 2011-07-15 09:17:14.293046424 +0200
***************
*** 1411,1416 ****
--- 1411,1436 ----
return PLPGSQL_RC_OK;
}
+
+ /* ----------
+ * Small routine that ensure a transformation char *str to text datum.
+ * Use a empty string, when str pointer is NULL.
+ * ----------
+ */
+ static void
+ exec_assign_dg_text(PLpgSQL_execstate *estate, PLpgSQL_datum *var, const char *cstr)
+ {
+ bool isnull = false;
+ Datum value;
+
+ if (cstr != NULL)
+ value = PointerGetDatum(cstring_to_text(cstr));
+ else
+ value = PointerGetDatum(cstring_to_text(""));
+
+ exec_assign_value(estate, var, value, TEXTOID, &isnull);
+ }
+
/* ----------
* exec_stmt_getdiag Put internal PG information into
* specified variables.
***************
*** 1435,1440 ****
--- 1455,1469 ----
if (var == NULL)
continue;
+ /*
+ * Stacked diagnostics statement is based on processing of Error Data.
+ * These data should be available.
+ */
+ if (stmt->is_stacked && estate->cur_error == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_STACKED_DIAGNOSTICS_ACCESSED_WITHOUT_ACTIVE_HANDLER),
+ errmsg("stacked diagnostics accessed without active handler")));
+
switch (diag_item->kind)
{
case PLPGSQL_GETDIAG_ROW_COUNT:
***************
*** 1445,1456 ****
break;
case PLPGSQL_GETDIAG_RESULT_OID:
-
exec_assign_value(estate, var,
ObjectIdGetDatum(estate->eval_lastoid),
OIDOID, &isnull);
break;
default:
elog(ERROR, "unrecognized attribute request: %d",
diag_item->kind);
--- 1474,1509 ----
break;
case PLPGSQL_GETDIAG_RESULT_OID:
exec_assign_value(estate, var,
ObjectIdGetDatum(estate->eval_lastoid),
OIDOID, &isnull);
break;
+ case PLPGSQL_GETDIAG_ERROR_DETAIL:
+ exec_assign_dg_text(estate, var,
+ estate->cur_error->detail);
+ break;
+
+ case PLPGSQL_GETDIAG_ERROR_HINT:
+ exec_assign_dg_text(estate, var,
+ estate->cur_error->hint);
+ break;
+
+ case PLPGSQL_GETDIAG_ERROR_CONTEXT:
+ exec_assign_dg_text(estate, var,
+ estate->cur_error->context);
+ break;
+
+ case PLPGSQL_GETDIAG_RETURNED_SQLSTATE:
+ exec_assign_dg_text(estate, var,
+ unpack_sql_state(estate->cur_error->sqlerrcode));
+ break;
+
+ case PLPGSQL_GETDIAG_MESSAGE_TEXT:
+ exec_assign_dg_text(estate, var,
+ estate->cur_error->message);
+ break;
+
default:
elog(ERROR, "unrecognized attribute request: %d",
diag_item->kind);
*** ./src/pl/plpgsql/src/pl_funcs.c.orig 2011-07-15 07:53:03.074787623 +0200
--- ./src/pl/plpgsql/src/pl_funcs.c 2011-07-15 08:49:08.397831023 +0200
***************
*** 265,270 ****
--- 265,296 ----
return "unknown";
}
+ /*
+ * Returns a name of diagnostic value for GET DIAGNOSTICS statement
+ */
+ const char *
+ plpgsql_getdiag_kindname(int kind)
+ {
+ switch (kind)
+ {
+ case PLPGSQL_GETDIAG_ROW_COUNT:
+ return "ROW_COUNT";
+ case PLPGSQL_GETDIAG_RESULT_OID:
+ return "RESULT_OID";
+ case PLPGSQL_GETDIAG_ERROR_CONTEXT:
+ return "PG_EXCEPTION_CONTEXT";
+ case PLPGSQL_GETDIAG_ERROR_DETAIL:
+ return "PG_EXCEPTION_DETAIL";
+ case PLPGSQL_GETDIAG_ERROR_HINT:
+ return "PG_EXCEPTION_HINT";
+ case PLPGSQL_GETDIAG_RETURNED_SQLSTATE:
+ return "RETURNED_SQLSTATE";
+ case PLPGSQL_GETDIAG_MESSAGE_TEXT:
+ return "MESSAGE_TEXT";
+ }
+
+ return "unknown";
+ };
/**********************************************************************
* Release memory when a PL/pgSQL function is no longer needed
***************
*** 1389,1395 ****
ListCell *lc;
dump_ind();
! printf("GET DIAGNOSTICS ");
foreach(lc, stmt->diag_items)
{
PLpgSQL_diag_item *diag_item = (PLpgSQL_diag_item *) lfirst(lc);
--- 1415,1421 ----
ListCell *lc;
dump_ind();
! printf("GET %s DIAGNOSTICS ", stmt->is_stacked ? "STACKED" : "CURRENT");
foreach(lc, stmt->diag_items)
{
PLpgSQL_diag_item *diag_item = (PLpgSQL_diag_item *) lfirst(lc);
***************
*** 1397,1418 ****
if (lc != list_head(stmt->diag_items))
printf(", ");
! printf("{var %d} = ", diag_item->target);
!
! switch (diag_item->kind)
! {
! case PLPGSQL_GETDIAG_ROW_COUNT:
! printf("ROW_COUNT");
! break;
!
! case PLPGSQL_GETDIAG_RESULT_OID:
! printf("RESULT_OID");
! break;
!
! default:
! printf("???");
! break;
! }
}
printf("\n");
}
--- 1423,1430 ----
if (lc != list_head(stmt->diag_items))
printf(", ");
! printf("{var %d} = %s", diag_item->target,
! plpgsql_getdiag_kindname(diag_item->kind));
}
printf("\n");
}
*** ./src/pl/plpgsql/src/plpgsql.h.orig 2011-07-15 07:53:03.077787597 +0200
--- ./src/pl/plpgsql/src/plpgsql.h 2011-07-15 08:48:01.908423020 +0200
***************
*** 126,132 ****
enum
{
PLPGSQL_GETDIAG_ROW_COUNT,
! PLPGSQL_GETDIAG_RESULT_OID
};
/* --------
--- 126,137 ----
enum
{
PLPGSQL_GETDIAG_ROW_COUNT,
! PLPGSQL_GETDIAG_RESULT_OID,
! PLPGSQL_GETDIAG_ERROR_CONTEXT,
! PLPGSQL_GETDIAG_ERROR_DETAIL,
! PLPGSQL_GETDIAG_ERROR_HINT,
! PLPGSQL_GETDIAG_RETURNED_SQLSTATE,
! PLPGSQL_GETDIAG_MESSAGE_TEXT
};
/* --------
***************
*** 377,382 ****
--- 382,388 ----
int cmd_type;
int lineno;
List *diag_items; /* List of PLpgSQL_diag_item */
+ bool is_stacked; /* true when it access a second area of diagnostics info */
} PLpgSQL_stmt_getdiag;
***************
*** 929,934 ****
--- 935,941 ----
* ----------
*/
extern const char *plpgsql_stmt_typename(PLpgSQL_stmt *stmt);
+ extern const char *plpgsql_getdiag_kindname(int kind);
extern void plpgsql_free_function_memory(PLpgSQL_function *func);
extern void plpgsql_dumptree(PLpgSQL_function *func);
*** ./src/pl/plpgsql/src/pl_scanner.c.orig 2011-07-15 07:53:03.075787614 +0200
--- ./src/pl/plpgsql/src/pl_scanner.c 2011-07-15 08:01:04.531609103 +0200
***************
*** 110,115 ****
--- 110,116 ----
PG_KEYWORD("array", K_ARRAY, UNRESERVED_KEYWORD)
PG_KEYWORD("backward", K_BACKWARD, UNRESERVED_KEYWORD)
PG_KEYWORD("constant", K_CONSTANT, UNRESERVED_KEYWORD)
+ PG_KEYWORD("current", K_CURRENT, UNRESERVED_KEYWORD)
PG_KEYWORD("cursor", K_CURSOR, UNRESERVED_KEYWORD)
PG_KEYWORD("debug", K_DEBUG, UNRESERVED_KEYWORD)
PG_KEYWORD("detail", K_DETAIL, UNRESERVED_KEYWORD)
***************
*** 124,143 ****
--- 125,150 ----
PG_KEYWORD("last", K_LAST, UNRESERVED_KEYWORD)
PG_KEYWORD("log", K_LOG, UNRESERVED_KEYWORD)
PG_KEYWORD("message", K_MESSAGE, UNRESERVED_KEYWORD)
+ PG_KEYWORD("message_text", K_MESSAGE_TEXT, UNRESERVED_KEYWORD)
PG_KEYWORD("next", K_NEXT, UNRESERVED_KEYWORD)
PG_KEYWORD("no", K_NO, UNRESERVED_KEYWORD)
PG_KEYWORD("notice", K_NOTICE, UNRESERVED_KEYWORD)
PG_KEYWORD("option", K_OPTION, UNRESERVED_KEYWORD)
+ PG_KEYWORD("pg_exception_context", K_EXCEPTION_CONTEXT, UNRESERVED_KEYWORD)
+ PG_KEYWORD("pg_exception_detail", K_EXCEPTION_DETAIL, UNRESERVED_KEYWORD)
+ PG_KEYWORD("pg_exception_hint", K_EXCEPTION_HINT, UNRESERVED_KEYWORD)
PG_KEYWORD("prior", K_PRIOR, UNRESERVED_KEYWORD)
PG_KEYWORD("query", K_QUERY, UNRESERVED_KEYWORD)
PG_KEYWORD("relative", K_RELATIVE, UNRESERVED_KEYWORD)
PG_KEYWORD("result_oid", K_RESULT_OID, UNRESERVED_KEYWORD)
+ PG_KEYWORD("returned_sqlstate", K_RETURNED_SQLSTATE, UNRESERVED_KEYWORD)
PG_KEYWORD("reverse", K_REVERSE, UNRESERVED_KEYWORD)
PG_KEYWORD("row_count", K_ROW_COUNT, UNRESERVED_KEYWORD)
PG_KEYWORD("rowtype", K_ROWTYPE, UNRESERVED_KEYWORD)
PG_KEYWORD("scroll", K_SCROLL, UNRESERVED_KEYWORD)
PG_KEYWORD("slice", K_SLICE, UNRESERVED_KEYWORD)
PG_KEYWORD("sqlstate", K_SQLSTATE, UNRESERVED_KEYWORD)
+ PG_KEYWORD("stacked", K_STACKED, UNRESERVED_KEYWORD)
PG_KEYWORD("type", K_TYPE, UNRESERVED_KEYWORD)
PG_KEYWORD("use_column", K_USE_COLUMN, UNRESERVED_KEYWORD)
PG_KEYWORD("use_variable", K_USE_VARIABLE, UNRESERVED_KEYWORD)
*** ./src/test/regress/expected/plpgsql.out.orig 2011-07-15 07:53:03.078787589 +0200
--- ./src/test/regress/expected/plpgsql.out 2011-07-15 08:01:04.534609076 +0200
***************
*** 4434,4436 ****
--- 4434,4511 ----
drop function foreach_test(anyarray);
drop type xy_tuple;
+ -- access to exception data
+ create or replace function bad_function()
+ returns int as $$
+ declare v int := 0;
+ begin
+ return 10 / v;
+ end;
+ $$ language plpgsql;
+ create or replace function raise_function()
+ returns void as $$
+ begin
+ raise exception 'custom exception'
+ using detail = 'some detail of custom exception',
+ hint = 'some hint related to custom exception';
+ end;
+ $$ language plpgsql;
+ create or replace function stacked_diagnostics_test01()
+ returns void as $$
+ declare _sqlstate text; _message text; _context text;
+ begin
+ perform bad_function();
+ exception when others then
+ get stacked diagnostics
+ _sqlstate = returned_sqlstate,
+ _message = message_text,
+ _context = pg_exception_context;
+ raise notice 'sqlstate: %, message: %, context: [%]', _sqlstate, _message, replace(_context, e'\n', ' <- ');
+ end;
+ $$ language plpgsql;
+ select stacked_diagnostics_test01();
+ NOTICE: sqlstate: 22012, message: division by zero, context: [PL/pgSQL function "bad_function" line 4 at RETURN <- SQL statement "SELECT bad_function()" <- PL/pgSQL function "stacked_diagnostics_test01" line 4 at PERFORM]
+ stacked_diagnostics_test01
+ ----------------------------
+
+ (1 row)
+
+ create or replace function stacked_diagnostics_test02()
+ returns void as $$
+ declare _detail text; _hint text; _message text;
+ begin
+ perform raise_function();
+ exception when others then
+ get stacked diagnostics
+ _message = message_text,
+ _detail = pg_exception_detail,
+ _hint = pg_exception_hint;
+ raise notice 'message: %, detail: %, hint: %', _message, _detail, _hint;
+ end;
+ $$ language plpgsql;
+ select stacked_diagnostics_test02();
+ NOTICE: message: custom exception, detail: some detail of custom exception, hint: some hint related to custom exception
+ stacked_diagnostics_test02
+ ----------------------------
+
+ (1 row)
+
+ -- should to fail, cannot use stacked diagnostics statement outside handler
+ create or replace function stacked_diagnostics_test02()
+ returns void as $$
+ declare _detail text; _hint text; _message text;
+ begin
+ get stacked diagnostics
+ _message = message_text,
+ _detail = pg_exception_detail,
+ _hint = pg_exception_hint;
+ raise notice 'message: %, detail: %, hint: %', _message, _detail, _hint;
+ end;
+ $$ language plpgsql;
+ select stacked_diagnostics_test02();
+ ERROR: stacked diagnostics accessed without active handler
+ CONTEXT: PL/pgSQL function "stacked_diagnostics_test02" line 4 at GET DIAGNOSTICS
+ drop function bad_function();
+ drop function raise_function();
+ drop function stacked_diagnostics_test01();
+ drop function stacked_diagnostics_test02();
*** ./src/test/regress/sql/plpgsql.sql.orig 2011-07-15 07:53:03.080787571 +0200
--- ./src/test/regress/sql/plpgsql.sql 2011-07-15 08:01:04.536609058 +0200
***************
*** 3489,3491 ****
--- 3489,3563 ----
drop function foreach_test(anyarray);
drop type xy_tuple;
+
+
+ -- access to exception data
+ create or replace function bad_function()
+ returns int as $$
+ declare v int := 0;
+ begin
+ return 10 / v;
+ end;
+ $$ language plpgsql;
+
+ create or replace function raise_function()
+ returns void as $$
+ begin
+ raise exception 'custom exception'
+ using detail = 'some detail of custom exception',
+ hint = 'some hint related to custom exception';
+ end;
+ $$ language plpgsql;
+
+ create or replace function stacked_diagnostics_test01()
+ returns void as $$
+ declare _sqlstate text; _message text; _context text;
+ begin
+ perform bad_function();
+ exception when others then
+ get stacked diagnostics
+ _sqlstate = returned_sqlstate,
+ _message = message_text,
+ _context = pg_exception_context;
+ raise notice 'sqlstate: %, message: %, context: [%]', _sqlstate, _message, replace(_context, e'\n', ' <- ');
+ end;
+ $$ language plpgsql;
+
+ select stacked_diagnostics_test01();
+
+ create or replace function stacked_diagnostics_test02()
+ returns void as $$
+ declare _detail text; _hint text; _message text;
+ begin
+ perform raise_function();
+ exception when others then
+ get stacked diagnostics
+ _message = message_text,
+ _detail = pg_exception_detail,
+ _hint = pg_exception_hint;
+ raise notice 'message: %, detail: %, hint: %', _message, _detail, _hint;
+ end;
+ $$ language plpgsql;
+
+ select stacked_diagnostics_test02();
+
+ -- should to fail, cannot use stacked diagnostics statement outside handler
+
+ create or replace function stacked_diagnostics_test02()
+ returns void as $$
+ declare _detail text; _hint text; _message text;
+ begin
+ get stacked diagnostics
+ _message = message_text,
+ _detail = pg_exception_detail,
+ _hint = pg_exception_hint;
+ raise notice 'message: %, detail: %, hint: %', _message, _detail, _hint;
+ end;
+ $$ language plpgsql;
+
+ select stacked_diagnostics_test02();
+
+ drop function bad_function();
+ drop function raise_function();
+ drop function stacked_diagnostics_test01();
+ drop function stacked_diagnostics_test02();