Hello
This proposal is related to exception processing. Inside exception
handler we can get some basic info about exception - message text and
message code. There are other fields - but these fields are no
available now in PL/pgSQL. The cheap access to fields inside ErrorData
structure can be implemented inside GET DIAGNOSTICS statements - this
statement is created for this purpose. I propose a new thee
identifiers, that can be used there: ERROR_DETAIL, ERROR_HINT and
ERROR_CONTEXT. Using is simple:
CREATE OR REPLACE FUNCTION foo()
RETURNS void AS $$
DECLARE
_detail text;
_hint text;
_context text;
BEGIN
RAISE EXCEPTION 'some message'
USING DETAIL = 'some message specific description',
HINT = 'some hint related to messgae';
EXCEPTION WHEN OTHERS THEN
GET DIAGNOSTICS _detail = ERROR_DETAIL,
_hint = ERROR_HINT,
_context = ERROR_CONTEXT;
RAISE WARNING 'caught message: %', SQLERRM
USING DETAIL = e'\ncaught detail: ' || _detail ||
e'\ncaught hint: ' || _hint ||
e'\ncaught context: ' || _context;
END;
$$ LANGUAGE plpgsql;
SELECT foo();
A implementation of ERROR_DETAIL and ERROR_HINT is simple and without
possible performance issues. It has zero impact on performance.
A implementation of ERROR_CONTEXT is not without impact on
performance, because context should be collected when exception is
caught. One solution is removing a ERROR_CONTEXT from proposal. Second
solution can be a design of enhanced syntax for exception trap like
(it means - collect CONTEXT when exception is handled)
BEGIN
EXCEPTION (ERROR_CONTEXT=true) WHEN OTHERS THEN
...
END
Getting a context can be a problem - but it is very important
information, that can significantly help with exception's explanation.
ideas, notes?
Regards
Pavel Stehule
*** ./src/pl/plpgsql/src/gram.y.orig 2011-05-18 19:41:56.755678378 +0200
--- ./src/pl/plpgsql/src/gram.y 2011-05-21 21:03:28.799168296 +0200
***************
*** 250,255 ****
--- 250,256 ----
%token <keyword> K_CLOSE
%token <keyword> K_COLLATE
%token <keyword> K_CONSTANT
+ %token <keyword> K_CONTEXT
%token <keyword> K_CONTINUE
%token <keyword> K_CURSOR
%token <keyword> K_DEBUG
***************
*** 263,268 ****
--- 264,272 ----
%token <keyword> K_END
%token <keyword> K_ERRCODE
%token <keyword> K_ERROR
+ %token <keyword> K_ERROR_CONTEXT
+ %token <keyword> K_ERROR_DETAIL
+ %token <keyword> K_ERROR_HINT
%token <keyword> K_EXCEPTION
%token <keyword> K_EXECUTE
%token <keyword> K_EXIT
***************
*** 877,882 ****
--- 881,895 ----
else if (tok_is_keyword(tok, &yylval,
K_RESULT_OID, "result_oid"))
$$ = PLPGSQL_GETDIAG_RESULT_OID;
+ else if (tok_is_keyword(tok, &yylval,
+ K_ERROR_DETAIL, "error_detail"))
+ $$ = PLPGSQL_GETDIAG_ERROR_DETAIL;
+ else if (tok_is_keyword(tok, &yylval,
+ K_ERROR_HINT, "error_hint"))
+ $$ = PLPGSQL_GETDIAG_ERROR_HINT;
+ else if (tok_is_keyword(tok, &yylval,
+ K_ERROR_CONTEXT, "error_context"))
+ $$ = PLPGSQL_GETDIAG_ERROR_CONTEXT;
else
yyerror("unrecognized GET DIAGNOSTICS item");
}
***************
*** 2141,2146 ****
--- 2154,2162 ----
| K_DUMP
| K_ERRCODE
| K_ERROR
+ | K_ERROR_CONTEXT
+ | K_ERROR_DETAIL
+ | K_ERROR_HINT
| K_FIRST
| K_FORWARD
| K_HINT
*** ./src/pl/plpgsql/src/pl_exec.c.orig 2011-05-18 19:42:15.458152167 +0200
--- ./src/pl/plpgsql/src/pl_exec.c 2011-05-21 21:49:02.593299349 +0200
***************
*** 1081,1089 ****
--- 1081,1097 ----
{
ErrorData *edata;
ListCell *e;
+ ErrorContextCallback *econtext;
+
estate->err_text = gettext_noop("during exception cleanup");
+ /* Collect a context info */
+ for (econtext = error_context_stack;
+ econtext != NULL;
+ econtext = econtext->previous)
+ (*econtext->callback) (econtext->arg);
+
/* Save error info */
MemoryContextSwitchTo(oldcontext);
edata = CopyErrorData();
***************
*** 1449,1454 ****
--- 1457,1504 ----
ObjectIdGetDatum(estate->eval_lastoid),
OIDOID, &isnull);
break;
+ case PLPGSQL_GETDIAG_ERROR_DETAIL:
+ case PLPGSQL_GETDIAG_ERROR_HINT:
+ case PLPGSQL_GETDIAG_ERROR_CONTEXT:
+ {
+ char *strval = NULL;
+ Datum value;
+
+ /*
+ * Now a fields based on processing of Error Data
+ * are handled.
+ */
+ if (estate->cur_error == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("access to exception's info outside exception handler")));
+ switch (diag_item->kind)
+ {
+ case PLPGSQL_GETDIAG_ERROR_DETAIL:
+ strval = estate->cur_error->detail;
+ break;
+ case PLPGSQL_GETDIAG_ERROR_HINT:
+ strval = estate->cur_error->hint;
+ break;
+ case PLPGSQL_GETDIAG_ERROR_CONTEXT:
+ strval = estate->cur_error->context;
+ break;
+ }
+
+ if (strval != NULL)
+ {
+ value = PointerGetDatum(cstring_to_text(strval));
+ }
+ else
+ {
+ isnull = true;
+ value = (Datum) 0;
+ }
+
+ exec_assign_value(estate, var,
+ value, TEXTOID, &isnull);
+ break;
+ }
default:
elog(ERROR, "unrecognized attribute request: %d",
*** ./src/pl/plpgsql/src/pl_funcs.c.orig 2011-05-21 20:59:52.557887117 +0200
--- ./src/pl/plpgsql/src/pl_funcs.c 2011-05-21 21:04:45.510124756 +0200
***************
*** 1409,1414 ****
--- 1409,1426 ----
printf("RESULT_OID");
break;
+ case PLPGSQL_GETDIAG_ERROR_CONTEXT:
+ printf("ERROR_CONTEXT");
+ break;
+
+ case PLPGSQL_GETDIAG_ERROR_DETAIL:
+ printf("ERROR_DETAIL");
+ break;
+
+ case PLPGSQL_GETDIAG_ERROR_HINT:
+ printf("ERROR_HINT");
+ break;
+
default:
printf("???");
break;
*** ./src/pl/plpgsql/src/plpgsql.h.orig 2011-05-18 19:42:06.402954431 +0200
--- ./src/pl/plpgsql/src/plpgsql.h 2011-05-21 20:58:17.139277824 +0200
***************
*** 126,132 ****
enum
{
PLPGSQL_GETDIAG_ROW_COUNT,
! PLPGSQL_GETDIAG_RESULT_OID
};
/* --------
--- 126,135 ----
enum
{
PLPGSQL_GETDIAG_ROW_COUNT,
! PLPGSQL_GETDIAG_RESULT_OID,
! PLPGSQL_GETDIAG_ERROR_CONTEXT,
! PLPGSQL_GETDIAG_ERROR_DETAIL,
! PLPGSQL_GETDIAG_ERROR_HINT
};
/* --------
*** ./src/pl/plpgsql/src/pl_scanner.c.orig 2011-05-18 14:22:29.000000000 +0200
--- ./src/pl/plpgsql/src/pl_scanner.c 2011-05-21 20:57:13.017519538 +0200
***************
*** 116,121 ****
--- 116,124 ----
PG_KEYWORD("dump", K_DUMP, UNRESERVED_KEYWORD)
PG_KEYWORD("errcode", K_ERRCODE, UNRESERVED_KEYWORD)
PG_KEYWORD("error", K_ERROR, UNRESERVED_KEYWORD)
+ PG_KEYWORD("error_context", K_ERROR_CONTEXT, UNRESERVED_KEYWORD)
+ PG_KEYWORD("error_detail", K_ERROR_DETAIL, UNRESERVED_KEYWORD)
+ PG_KEYWORD("error_hint", K_ERROR_HINT, UNRESERVED_KEYWORD)
PG_KEYWORD("first", K_FIRST, UNRESERVED_KEYWORD)
PG_KEYWORD("forward", K_FORWARD, UNRESERVED_KEYWORD)
PG_KEYWORD("hint", K_HINT, UNRESERVED_KEYWORD)
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers