1
0
mirror of https://github.com/postgres/postgres.git synced 2025-09-03 15:22:11 +03:00

Add GET STACKED DIAGNOSTICS plpgsql command to retrieve exception info.

This is more SQL-spec-compliant, more easily extensible, and better
performing than the old method of inventing special variables.

Pavel Stehule, reviewed by Shigeru Hanada and David Wheeler
This commit is contained in:
Tom Lane
2011-07-18 14:46:27 -04:00
parent 3406dd22fd
commit 3d4890c0c5
9 changed files with 480 additions and 66 deletions

View File

@@ -203,6 +203,7 @@ static List *read_raise_options(void);
%type <casewhen> case_when
%type <list> case_when_list opt_case_else
%type <boolean> getdiag_area_opt
%type <list> getdiag_list
%type <diagitem> getdiag_list_item
%type <ival> getdiag_item getdiag_target
@@ -251,6 +252,7 @@ static List *read_raise_options(void);
%token <keyword> K_COLLATE
%token <keyword> K_CONSTANT
%token <keyword> K_CONTINUE
%token <keyword> K_CURRENT
%token <keyword> K_CURSOR
%token <keyword> K_DEBUG
%token <keyword> K_DECLARE
@@ -284,6 +286,7 @@ static List *read_raise_options(void);
%token <keyword> K_LOG
%token <keyword> K_LOOP
%token <keyword> K_MESSAGE
%token <keyword> K_MESSAGE_TEXT
%token <keyword> K_MOVE
%token <keyword> K_NEXT
%token <keyword> K_NO
@@ -294,18 +297,23 @@ static List *read_raise_options(void);
%token <keyword> K_OPTION
%token <keyword> K_OR
%token <keyword> K_PERFORM
%token <keyword> K_PG_EXCEPTION_CONTEXT
%token <keyword> K_PG_EXCEPTION_DETAIL
%token <keyword> K_PG_EXCEPTION_HINT
%token <keyword> K_PRIOR
%token <keyword> K_QUERY
%token <keyword> K_RAISE
%token <keyword> K_RELATIVE
%token <keyword> K_RESULT_OID
%token <keyword> K_RETURN
%token <keyword> K_RETURNED_SQLSTATE
%token <keyword> K_REVERSE
%token <keyword> K_ROWTYPE
%token <keyword> K_ROW_COUNT
%token <keyword> K_SCROLL
%token <keyword> K_SLICE
%token <keyword> K_SQLSTATE
%token <keyword> K_STACKED
%token <keyword> K_STRICT
%token <keyword> K_THEN
%token <keyword> K_TO
@@ -832,19 +840,74 @@ stmt_assign : assign_var assign_operator expr_until_semi
}
;
stmt_getdiag : K_GET K_DIAGNOSTICS getdiag_list ';'
stmt_getdiag : K_GET getdiag_area_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->diag_items = $3;
new->is_stacked = $2;
new->diag_items = $4;
/*
* Check information items are valid for area option.
*/
foreach(lc, new->diag_items)
{
PLpgSQL_diag_item *ditem = (PLpgSQL_diag_item *) lfirst(lc);
switch (ditem->kind)
{
/* these fields are disallowed in stacked case */
case PLPGSQL_GETDIAG_ROW_COUNT:
case PLPGSQL_GETDIAG_RESULT_OID:
if (new->is_stacked)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("diagnostics item %s is not allowed in GET STACKED DIAGNOSTICS",
plpgsql_getdiag_kindname(ditem->kind)),
parser_errposition(@1)));
break;
/* these fields are disallowed in current case */
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:
if (!new->is_stacked)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("diagnostics item %s is not allowed in GET CURRENT DIAGNOSTICS",
plpgsql_getdiag_kindname(ditem->kind)),
parser_errposition(@1)));
break;
default:
elog(ERROR, "unrecognized diagnostic item kind: %d",
ditem->kind);
break;
}
}
$$ = (PLpgSQL_stmt *)new;
}
;
getdiag_area_opt :
{
$$ = false;
}
| K_CURRENT
{
$$ = false;
}
| K_STACKED
{
$$ = true;
}
;
getdiag_list : getdiag_list ',' getdiag_list_item
{
$$ = lappend($1, $3);
@@ -877,6 +940,21 @@ getdiag_item :
else if (tok_is_keyword(tok, &yylval,
K_RESULT_OID, "result_oid"))
$$ = PLPGSQL_GETDIAG_RESULT_OID;
else if (tok_is_keyword(tok, &yylval,
K_PG_EXCEPTION_DETAIL, "pg_exception_detail"))
$$ = PLPGSQL_GETDIAG_ERROR_DETAIL;
else if (tok_is_keyword(tok, &yylval,
K_PG_EXCEPTION_HINT, "pg_exception_hint"))
$$ = PLPGSQL_GETDIAG_ERROR_HINT;
else if (tok_is_keyword(tok, &yylval,
K_PG_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,6 +2213,7 @@ unreserved_keyword :
| K_ARRAY
| K_BACKWARD
| K_CONSTANT
| K_CURRENT
| K_CURSOR
| K_DEBUG
| K_DETAIL
@@ -2149,20 +2228,26 @@ unreserved_keyword :
| K_LAST
| K_LOG
| K_MESSAGE
| K_MESSAGE_TEXT
| K_NEXT
| K_NO
| K_NOTICE
| K_OPTION
| K_PG_EXCEPTION_CONTEXT
| K_PG_EXCEPTION_DETAIL
| K_PG_EXCEPTION_HINT
| K_PRIOR
| 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

View File

@@ -151,6 +151,9 @@ static bool exec_eval_simple_expr(PLpgSQL_execstate *estate,
static void exec_assign_expr(PLpgSQL_execstate *estate,
PLpgSQL_datum *target,
PLpgSQL_expr *expr);
static void exec_assign_c_string(PLpgSQL_execstate *estate,
PLpgSQL_datum *target,
const char *str);
static void exec_assign_value(PLpgSQL_execstate *estate,
PLpgSQL_datum *target,
Datum value, Oid valtype, bool *isNull);
@@ -1421,6 +1424,17 @@ exec_stmt_getdiag(PLpgSQL_execstate *estate, PLpgSQL_stmt_getdiag *stmt)
{
ListCell *lc;
/*
* GET STACKED DIAGNOSTICS is only valid inside an exception handler.
*
* Note: we trust the grammar to have disallowed the relevant item kinds
* if not is_stacked, otherwise we'd dump core below.
*/
if (stmt->is_stacked && estate->cur_error == NULL)
ereport(ERROR,
(errcode(ERRCODE_STACKED_DIAGNOSTICS_ACCESSED_WITHOUT_ACTIVE_HANDLER),
errmsg("GET STACKED DIAGNOSTICS cannot be used outside an exception handler")));
foreach(lc, stmt->diag_items)
{
PLpgSQL_diag_item *diag_item = (PLpgSQL_diag_item *) lfirst(lc);
@@ -1438,21 +1452,44 @@ exec_stmt_getdiag(PLpgSQL_execstate *estate, PLpgSQL_stmt_getdiag *stmt)
switch (diag_item->kind)
{
case PLPGSQL_GETDIAG_ROW_COUNT:
exec_assign_value(estate, var,
UInt32GetDatum(estate->eval_processed),
INT4OID, &isnull);
break;
case PLPGSQL_GETDIAG_RESULT_OID:
exec_assign_value(estate, var,
ObjectIdGetDatum(estate->eval_lastoid),
OIDOID, &isnull);
break;
case PLPGSQL_GETDIAG_ERROR_CONTEXT:
exec_assign_c_string(estate, var,
estate->cur_error->context);
break;
case PLPGSQL_GETDIAG_ERROR_DETAIL:
exec_assign_c_string(estate, var,
estate->cur_error->detail);
break;
case PLPGSQL_GETDIAG_ERROR_HINT:
exec_assign_c_string(estate, var,
estate->cur_error->hint);
break;
case PLPGSQL_GETDIAG_RETURNED_SQLSTATE:
exec_assign_c_string(estate, var,
unpack_sql_state(estate->cur_error->sqlerrcode));
break;
case PLPGSQL_GETDIAG_MESSAGE_TEXT:
exec_assign_c_string(estate, var,
estate->cur_error->message);
break;
default:
elog(ERROR, "unrecognized attribute request: %d",
elog(ERROR, "unrecognized diagnostic item kind: %d",
diag_item->kind);
}
}
@@ -2634,7 +2671,7 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
ReThrowError(estate->cur_error);
/* oops, we're not inside a handler */
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
(errcode(ERRCODE_STACKED_DIAGNOSTICS_ACCESSED_WITHOUT_ACTIVE_HANDLER),
errmsg("RAISE without parameters cannot be used outside an exception handler")));
}
@@ -3650,8 +3687,7 @@ exec_stmt_close(PLpgSQL_execstate *estate, PLpgSQL_stmt_close *stmt)
/* ----------
* exec_assign_expr Put an expression's result into
* a variable.
* exec_assign_expr Put an expression's result into a variable.
* ----------
*/
static void
@@ -3668,6 +3704,29 @@ exec_assign_expr(PLpgSQL_execstate *estate, PLpgSQL_datum *target,
}
/* ----------
* exec_assign_c_string Put a C string into a text variable.
*
* We take a NULL pointer as signifying empty string, not SQL null.
* ----------
*/
static void
exec_assign_c_string(PLpgSQL_execstate *estate, PLpgSQL_datum *target,
const char *str)
{
text *value;
bool isnull = false;
if (str != NULL)
value = cstring_to_text(str);
else
value = cstring_to_text("");
exec_assign_value(estate, target, PointerGetDatum(value),
TEXTOID, &isnull);
pfree(value);
}
/* ----------
* exec_assign_value Put a value into a target field
*

View File

@@ -265,6 +265,33 @@ plpgsql_stmt_typename(PLpgSQL_stmt *stmt)
return "unknown";
}
/*
* GET DIAGNOSTICS item name as a string, for use in error messages etc.
*/
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,7 +1416,7 @@ dump_getdiag(PLpgSQL_stmt_getdiag *stmt)
ListCell *lc;
dump_ind();
printf("GET DIAGNOSTICS ");
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,22 +1424,8 @@ dump_getdiag(PLpgSQL_stmt_getdiag *stmt)
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("{var %d} = %s", diag_item->target,
plpgsql_getdiag_kindname(diag_item->kind));
}
printf("\n");
}

View File

@@ -110,6 +110,7 @@ static const ScanKeyword unreserved_keywords[] = {
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,20 +125,26 @@ static const ScanKeyword unreserved_keywords[] = {
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_PG_EXCEPTION_CONTEXT, UNRESERVED_KEYWORD)
PG_KEYWORD("pg_exception_detail", K_PG_EXCEPTION_DETAIL, UNRESERVED_KEYWORD)
PG_KEYWORD("pg_exception_hint", K_PG_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)

View File

@@ -120,13 +120,18 @@ enum
};
/* ----------
* GET DIAGNOSTICS system attrs
* GET DIAGNOSTICS information items
* ----------
*/
enum
{
PLPGSQL_GETDIAG_ROW_COUNT,
PLPGSQL_GETDIAG_RESULT_OID
PLPGSQL_GETDIAG_RESULT_OID,
PLPGSQL_GETDIAG_ERROR_CONTEXT,
PLPGSQL_GETDIAG_ERROR_DETAIL,
PLPGSQL_GETDIAG_ERROR_HINT,
PLPGSQL_GETDIAG_RETURNED_SQLSTATE,
PLPGSQL_GETDIAG_MESSAGE_TEXT
};
/* --------
@@ -376,6 +381,7 @@ typedef struct
{ /* Get Diagnostics statement */
int cmd_type;
int lineno;
bool is_stacked; /* STACKED or CURRENT diagnostics area? */
List *diag_items; /* List of PLpgSQL_diag_item */
} PLpgSQL_stmt_getdiag;
@@ -929,6 +935,7 @@ extern PLpgSQL_nsitem *plpgsql_ns_lookup_label(PLpgSQL_nsitem *ns_cur,
* ----------
*/
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);