eelog-plpgsql-2012-07-06.patch

application/octet-stream

Filename: eelog-plpgsql-2012-07-06.patch
Type: application/octet-stream
Part: 0
Message: Re: enhanced error fields

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
Series: patch 2012
File+
doc/src/sgml/plpgsql.sgml 71 2
src/pl/plpgsql/src/gram.y 99 0
src/pl/plpgsql/src/pl_comp.c 3 0
src/pl/plpgsql/src/pl_exec.c 210 1
src/pl/plpgsql/src/pl_funcs.c 55 0
src/pl/plpgsql/src/plpgsql.h 30 2
src/pl/plpgsql/src/pl_scanner.c 11 0
src/test/regress/expected/plpgsql.out 65 0
src/test/regress/sql/plpgsql.sql 60 0
diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml
index ba2c57b..a8f9a85 100644
--- a/doc/src/sgml/plpgsql.sgml
+++ b/doc/src/sgml/plpgsql.sgml
@@ -2640,6 +2640,61 @@ GET STACKED DIAGNOSTICS <replaceable>variable</replaceable> = <replaceable>item<
          <entry>text</entry>
          <entry>line(s) of text describing the call stack</entry>
         </row>
+        <row>
+         <entry><literal>COLUMN_NAME</literal></entry>
+         <entry>text</entry>
+         <entry>the name of column related to error, if any</entry>
+        </row>
+        <row>
+         <entry><literal>TABLE_NAME</literal></entry>
+         <entry>text</entry>
+         <entry>the name of table related to error, if any</entry>
+        </row>
+        <row>
+         <entry><literal>SCHEMA_NAME</literal></entry>
+         <entry>text</entry>
+         <entry>the name table's schema related to error, if any</entry>
+        </row>
+        <row>
+         <entry><literal>CONSTRAINT_NAME</literal></entry>
+         <entry>text</entry>
+         <entry>the name of constraint related to error, if any</entry>
+        </row>
+        <row>
+         <entry><literal>CONSTRAINT_TABLE</literal></entry>
+         <entry>text</entry>
+         <entry>the table of constraint related to error, if any</entry>
+        </row>
+        <row>
+         <entry><literal>CONSTRAINT_SCHEMA</literal></entry>
+         <entry>text</entry>
+         <entry>the constaint's schema related to error, if any</entry>
+        </row>
+        <row>
+         <entry><literal>ROUTINE_NAME</literal></entry>
+         <entry>text</entry>
+         <entry>the name of routine related to error, if any</entry>
+        </row>
+        <row>
+         <entry><literal>ROUTINE_SCHEMA</literal></entry>
+         <entry>text</entry>
+         <entry>the schema of routine related to error, if any</entry>
+        </row>
+        <row>
+         <entry><literal>TRIGGER_NAME</literal></entry>
+         <entry>text</entry>
+         <entry>the name of trigger related to error, if any</entry>
+        </row>
+        <row>
+         <entry><literal>TRIGGER_TABLE</literal></entry>
+         <entry>text</entry>
+         <entry>the table of trigger related to error, if any</entry>
+        </row>
+        <row>
+         <entry><literal>TRIGGER_SCHEMA</literal></entry>
+         <entry>text</entry>
+         <entry>the schema of trigger related to error, if any</entry>
+        </row>
        </tbody>
       </tgroup>
      </table>
@@ -3277,7 +3332,11 @@ RAISE NOTICE 'Calling cs_create_job(%)', v_job_id;
     class="parameter">expression</replaceable> items.  The allowed
     <replaceable class="parameter">option</replaceable> keywords are
     <literal>MESSAGE</>, <literal>DETAIL</>, <literal>HINT</>, and
-    <literal>ERRCODE</>, while each <replaceable
+    , <literal>COLUMN_NAME</>, <literal>TABLE_NAME</>,
+    <literal>SCHEMA_NAME</>, <literal>CONSTRAINT_NAME</>,
+    <literal>CONSTRAINT_TABLE</literal>, <literal>CONSTRAINT_SCHEMA</>, 
+    <literal>ROUTINE_NAME</>, <literal>ROUTINE_SCHEMA</>, <literal>TRIGGER_NAME</>,
+    <literal>TRIGGER_TABLE</literal>, <literal>TRIGGER_SCHEMA</> while each <replaceable
     class="parameter">expression</replaceable> can be any string-valued
     expression.
     <literal>MESSAGE</> sets the error message text (this option can't
@@ -3287,7 +3346,17 @@ RAISE NOTICE 'Calling cs_create_job(%)', v_job_id;
     <literal>HINT</> supplies a hint message.
     <literal>ERRCODE</> specifies the error code (SQLSTATE) to report,
     either by condition name as shown in <xref linkend="errcodes-appendix">,
-    or directly as a five-character SQLSTATE code.
+    or directly as a five-character SQLSTATE code. <literal>COLUMN_NAME</>
+    sets the name of column related to error. <literal>TABLE_NAME</>
+    sets the name of table related to error. <literal>SCHEMA_NAME</> sets
+    the schema of table related to error. <literal>CONSTRAINT_NAME</> sets
+    name of constraint. <literal>CONSTRAINT_TABLE</literal> sets a table
+    related to constraint. <literal>CONSTRAINT_SCHEMA</> sets a schema of constraint
+    related to error. <literal>ROUTINE_NAME</> sets a name of routine related
+    to error. <literal>ROUTINE_SCHEMA</> sets a schema of routine related to error.
+    <literal>TRIGGER_NAME</> sets a name of trigger related to error.
+    <literal>TRIGGER_TABLE</> sets a table of trigger related to error.
+    <literal>TRIGGER_SCHEMA</> sets a schema of trigger related to error.
    </para>
 
    <para>
diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y
index 4b2dce8..e0e9970 100644
--- a/src/pl/plpgsql/src/gram.y
+++ b/src/pl/plpgsql/src/gram.y
@@ -259,6 +259,17 @@ static	List			*read_raise_options(void);
 %token <keyword>	K_DECLARE
 %token <keyword>	K_DEFAULT
 %token <keyword>	K_DETAIL
+%token <keyword>	K_DIAG_COLUMN_NAME
+%token <keyword>	K_DIAG_TABLE_NAME
+%token <keyword>	K_DIAG_SCHEMA_NAME
+%token <keyword>	K_DIAG_CONSTRAINT_NAME
+%token <keyword>	K_DIAG_CONSTRAINT_TABLE
+%token <keyword>	K_DIAG_CONSTRAINT_SCHEMA
+%token <keyword>	K_DIAG_ROUTINE_NAME
+%token <keyword>	K_DIAG_ROUTINE_SCHEMA
+%token <keyword>	K_DIAG_TRIGGER_NAME
+%token <keyword>	K_DIAG_TRIGGER_TABLE
+%token <keyword>	K_DIAG_TRIGGER_SCHEMA
 %token <keyword>	K_DIAGNOSTICS
 %token <keyword>	K_DUMP
 %token <keyword>	K_ELSE
@@ -877,6 +888,17 @@ stmt_getdiag	: K_GET getdiag_area_opt K_DIAGNOSTICS getdiag_list ';'
 								case PLPGSQL_GETDIAG_ERROR_HINT:
 								case PLPGSQL_GETDIAG_RETURNED_SQLSTATE:
 								case PLPGSQL_GETDIAG_MESSAGE_TEXT:
+								case PLPGSQL_GETDIAG_COLUMN_NAME:
+								case PLPGSQL_GETDIAG_TABLE_NAME:
+								case PLPGSQL_GETDIAG_SCHEMA_NAME:
+								case PLPGSQL_GETDIAG_CONSTRAINT_NAME:
+								case PLPGSQL_GETDIAG_CONSTRAINT_TABLE:
+								case PLPGSQL_GETDIAG_CONSTRAINT_SCHEMA:
+								case PLPGSQL_GETDIAG_ROUTINE_NAME:
+								case PLPGSQL_GETDIAG_ROUTINE_SCHEMA:
+								case PLPGSQL_GETDIAG_TRIGGER_NAME:
+								case PLPGSQL_GETDIAG_TRIGGER_TABLE:
+								case PLPGSQL_GETDIAG_TRIGGER_SCHEMA:
 									if (!new->is_stacked)
 										ereport(ERROR,
 												(errcode(ERRCODE_SYNTAX_ERROR),
@@ -956,6 +978,39 @@ getdiag_item :
 						else if (tok_is_keyword(tok, &yylval,
 												K_RETURNED_SQLSTATE, "returned_sqlstate"))
 							$$ = PLPGSQL_GETDIAG_RETURNED_SQLSTATE;
+						else if (tok_is_keyword(tok, &yylval,
+												K_DIAG_COLUMN_NAME, "column_name"))
+							$$ = PLPGSQL_GETDIAG_COLUMN_NAME;
+						else if (tok_is_keyword(tok, &yylval,
+												K_DIAG_TABLE_NAME, "table_name"))
+							$$ = PLPGSQL_GETDIAG_TABLE_NAME;
+						else if (tok_is_keyword(tok, &yylval,
+												K_DIAG_SCHEMA_NAME, "schema_name"))
+							$$ = PLPGSQL_GETDIAG_SCHEMA_NAME;
+						else if (tok_is_keyword(tok, &yylval,
+												K_DIAG_CONSTRAINT_NAME, "constraint_name"))
+							$$ = PLPGSQL_GETDIAG_CONSTRAINT_NAME;
+						else if (tok_is_keyword(tok, &yylval,
+												K_DIAG_CONSTRAINT_TABLE, "constraint_table"))
+							$$ = PLPGSQL_GETDIAG_CONSTRAINT_TABLE;
+						else if (tok_is_keyword(tok, &yylval,
+												K_DIAG_CONSTRAINT_SCHEMA, "constraint_schema"))
+							$$ = PLPGSQL_GETDIAG_CONSTRAINT_SCHEMA;
+						else if (tok_is_keyword(tok, &yylval,
+												K_DIAG_ROUTINE_NAME, "routine_name"))
+							$$ = PLPGSQL_GETDIAG_ROUTINE_NAME;
+						else if (tok_is_keyword(tok, &yylval,
+												K_DIAG_ROUTINE_SCHEMA, "routine_schema"))
+							$$ = PLPGSQL_GETDIAG_ROUTINE_SCHEMA;
+						else if (tok_is_keyword(tok, &yylval,
+												K_DIAG_TRIGGER_NAME, "trigger_name"))
+							$$ = PLPGSQL_GETDIAG_TRIGGER_NAME;
+						else if (tok_is_keyword(tok, &yylval,
+												K_DIAG_TRIGGER_TABLE, "trigger_table"))
+							$$ = PLPGSQL_GETDIAG_TRIGGER_TABLE;
+						else if (tok_is_keyword(tok, &yylval,
+												K_DIAG_TRIGGER_SCHEMA, "trigger_schema"))
+							$$ = PLPGSQL_GETDIAG_TRIGGER_SCHEMA;
 						else
 							yyerror("unrecognized GET DIAGNOSTICS item");
 					}
@@ -2204,7 +2259,11 @@ unreserved_keyword	:
 				| K_ALIAS
 				| K_ARRAY
 				| K_BACKWARD
+				| K_DIAG_COLUMN_NAME
 				| K_CONSTANT
+				| K_DIAG_CONSTRAINT_NAME
+				| K_DIAG_CONSTRAINT_TABLE
+				| K_DIAG_CONSTRAINT_SCHEMA
 				| K_CURRENT
 				| K_CURSOR
 				| K_DEBUG
@@ -2234,12 +2293,19 @@ unreserved_keyword	:
 				| K_RESULT_OID
 				| K_RETURNED_SQLSTATE
 				| K_REVERSE
+				| K_DIAG_ROUTINE_NAME
+				| K_DIAG_ROUTINE_SCHEMA
 				| K_ROW_COUNT
 				| K_ROWTYPE
+				| K_DIAG_SCHEMA_NAME
 				| K_SCROLL
 				| K_SLICE
 				| K_SQLSTATE
 				| K_STACKED
+				| K_DIAG_TABLE_NAME
+				| K_DIAG_TRIGGER_NAME
+				| K_DIAG_TRIGGER_TABLE
+				| K_DIAG_TRIGGER_SCHEMA
 				| K_TYPE
 				| K_USE_COLUMN
 				| K_USE_VARIABLE
@@ -3586,6 +3652,39 @@ read_raise_options(void)
 		else if (tok_is_keyword(tok, &yylval,
 								K_HINT, "hint"))
 			opt->opt_type = PLPGSQL_RAISEOPTION_HINT;
+		else if (tok_is_keyword(tok, &yylval,
+								K_DIAG_COLUMN_NAME, "column_name"))
+			opt->opt_type = PLPGSQL_RAISEOPTION_COLUMN_NAME;
+		else if (tok_is_keyword(tok, &yylval,
+								K_DIAG_TABLE_NAME, "table_name"))
+			opt->opt_type = PLPGSQL_RAISEOPTION_TABLE_NAME;
+		else if (tok_is_keyword(tok, &yylval,
+								K_DIAG_SCHEMA_NAME, "schema_name"))
+			opt->opt_type = PLPGSQL_RAISEOPTION_SCHEMA_NAME;
+		else if (tok_is_keyword(tok, &yylval,
+								K_DIAG_CONSTRAINT_NAME, "constraint_name"))
+			opt->opt_type = PLPGSQL_RAISEOPTION_CONSTRAINT_NAME;
+		else if (tok_is_keyword(tok, &yylval,
+								K_DIAG_CONSTRAINT_TABLE, "constraint_table"))
+			opt->opt_type = PLPGSQL_RAISEOPTION_CONSTRAINT_TABLE;
+		else if (tok_is_keyword(tok, &yylval,
+								K_DIAG_CONSTRAINT_SCHEMA, "constraint_schema"))
+			opt->opt_type = PLPGSQL_RAISEOPTION_CONSTRAINT_SCHEMA;
+		else if (tok_is_keyword(tok, &yylval,
+								K_DIAG_ROUTINE_NAME, "routine_name"))
+			opt->opt_type = PLPGSQL_RAISEOPTION_ROUTINE_NAME;
+		else if (tok_is_keyword(tok, &yylval,
+								K_DIAG_ROUTINE_SCHEMA, "routine_schema"))
+			opt->opt_type = PLPGSQL_RAISEOPTION_ROUTINE_SCHEMA;
+		else if (tok_is_keyword(tok, &yylval,
+								K_DIAG_TRIGGER_NAME, "trigger_name"))
+			opt->opt_type = PLPGSQL_RAISEOPTION_TRIGGER_NAME;
+		else if (tok_is_keyword(tok, &yylval,
+								K_DIAG_TRIGGER_TABLE, "trigger_table"))
+			opt->opt_type = PLPGSQL_RAISEOPTION_TRIGGER_TABLE;
+		else if (tok_is_keyword(tok, &yylval,
+								K_DIAG_TRIGGER_SCHEMA, "trigger_schema"))
+			opt->opt_type = PLPGSQL_RAISEOPTION_TRIGGER_SCHEMA;
 		else
 			yyerror("unrecognized RAISE statement option");
 
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 5d2f818..eb20b7a 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -342,6 +342,9 @@ do_compile(FunctionCallInfo fcinfo,
 	compile_tmp_cxt = MemoryContextSwitchTo(func_cxt);
 
 	function->fn_signature = format_procedure(fcinfo->flinfo->fn_oid);
+	function->fn_name = pstrdup(NameStr(procStruct->proname));
+	function->fn_schema = get_namespace_name(procStruct->pronamespace);
+
 	function->fn_oid = fcinfo->flinfo->fn_oid;
 	function->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
 	function->fn_tid = procTup->t_self;
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 8ca791c..2549d60 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -567,6 +567,9 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
 	else
 		elog(ERROR, "unrecognized trigger action: not INSERT, DELETE, or UPDATE");
 
+	estate.tg_name = trigdata->tg_trigger->tgname;
+	estate.tg_schema = get_namespace_name(RelationGetNamespace(trigdata->tg_relation));
+
 	/*
 	 * Assign the special tg_ variables
 	 */
@@ -782,6 +785,22 @@ plpgsql_exec_error_callback(void *arg)
 {
 	PLpgSQL_execstate *estate = (PLpgSQL_execstate *) arg;
 
+	/*
+	 * If we are on top of error callback stack, then set function and
+	 * trigger related diagnostics fields.
+	 */
+	if (error_context_stack->arg == arg)
+	{
+		erritem(PG_DIAG_ROUTINE_NAME, estate->func->fn_name);
+		erritem(PG_DIAG_ROUTINE_SCHEMA, estate->func->fn_schema);
+
+		if (estate->func->fn_is_trigger)
+		{
+			erritem(PG_DIAG_TRIGGER_NAME, estate->tg_name);
+			erritem(PG_DIAG_TRIGGER_SCHEMA, estate->tg_schema);
+		}
+	}
+
 	/* if we are doing RAISE, don't report its location */
 	if (estate->err_text == raise_skip_msg)
 		return;
@@ -1489,6 +1508,61 @@ exec_stmt_getdiag(PLpgSQL_execstate *estate, PLpgSQL_stmt_getdiag *stmt)
 									 estate->cur_error->message);
 				break;
 
+			case PLPGSQL_GETDIAG_COLUMN_NAME:
+				exec_assign_c_string(estate, var,
+									 estate->cur_error->column_name);
+				break;
+
+			case PLPGSQL_GETDIAG_TABLE_NAME:
+				exec_assign_c_string(estate, var,
+									 estate->cur_error->table_name);
+				break;
+
+			case PLPGSQL_GETDIAG_SCHEMA_NAME:
+				exec_assign_c_string(estate, var,
+									 estate->cur_error->schema_name);
+				break;
+
+			case PLPGSQL_GETDIAG_CONSTRAINT_NAME:
+				exec_assign_c_string(estate, var,
+									 estate->cur_error->constraint_name);
+				break;
+
+			case PLPGSQL_GETDIAG_CONSTRAINT_TABLE:
+				exec_assign_c_string(estate, var,
+									 estate->cur_error->constraint_table);
+				break;
+
+			case PLPGSQL_GETDIAG_CONSTRAINT_SCHEMA:
+				exec_assign_c_string(estate, var,
+									 estate->cur_error->constraint_schema);
+				break;
+
+			case PLPGSQL_GETDIAG_ROUTINE_NAME:
+				exec_assign_c_string(estate, var,
+									 estate->cur_error->routine_name);
+				break;
+
+			case PLPGSQL_GETDIAG_ROUTINE_SCHEMA:
+				exec_assign_c_string(estate, var,
+									 estate->cur_error->routine_schema);
+				break;
+
+			case PLPGSQL_GETDIAG_TRIGGER_NAME:
+				exec_assign_c_string(estate, var,
+									 estate->cur_error->trigger_name);
+				break;
+
+			case PLPGSQL_GETDIAG_TRIGGER_TABLE:
+				exec_assign_c_string(estate, var,
+									 estate->cur_error->trigger_table);
+				break;
+
+			case PLPGSQL_GETDIAG_TRIGGER_SCHEMA:
+				exec_assign_c_string(estate, var,
+									 estate->cur_error->trigger_schema);
+				break;
+
 			default:
 				elog(ERROR, "unrecognized diagnostic item kind: %d",
 					 diag_item->kind);
@@ -2666,6 +2740,17 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
 	char	   *err_message = NULL;
 	char	   *err_detail = NULL;
 	char	   *err_hint = NULL;
+	char	   *column_name = NULL;
+	char	   *table_name = NULL;
+	char	   *schema_name = NULL;
+	char	   *constraint_name = NULL;
+	char	   *constraint_table = NULL;
+	char	   *constraint_schema = NULL;
+	char	   *routine_name = NULL;
+	char	   *routine_schema = NULL;
+	char	   *trigger_name = NULL;
+	char	   *trigger_table = NULL;
+	char	   *trigger_schema = NULL;
 	ListCell   *lc;
 
 	/* RAISE with no parameters: re-throw current exception */
@@ -2805,6 +2890,94 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
 									"HINT")));
 				err_hint = pstrdup(extval);
 				break;
+			case PLPGSQL_RAISEOPTION_COLUMN_NAME:
+				if (column_name)
+					ereport(ERROR,
+							(errcode(ERRCODE_SYNTAX_ERROR),
+							 errmsg("RAISE option already specified: %s",
+									"COLUMN_NAME")));
+				column_name = pstrdup(extval);
+				break;
+			case PLPGSQL_RAISEOPTION_TABLE_NAME:
+				if (table_name)
+					ereport(ERROR,
+							(errcode(ERRCODE_SYNTAX_ERROR),
+							 errmsg("RAISE option already specified: %s",
+									"TABLE_NAME")));
+				table_name = pstrdup(extval);
+				break;
+			case PLPGSQL_RAISEOPTION_SCHEMA_NAME:
+				if (schema_name)
+					ereport(ERROR,
+							(errcode(ERRCODE_SYNTAX_ERROR),
+							 errmsg("RAISE option already specified: %s",
+									"SCHEMA_NAME")));
+				schema_name = pstrdup(extval);
+				break;
+			case PLPGSQL_RAISEOPTION_CONSTRAINT_NAME:
+				if (constraint_name)
+					ereport(ERROR,
+							(errcode(ERRCODE_SYNTAX_ERROR),
+							 errmsg("RAISE option already specified: %s",
+									"CONSTRAINT_NAME")));
+				constraint_name = pstrdup(extval);
+				break;
+			case PLPGSQL_RAISEOPTION_CONSTRAINT_TABLE:
+				if (constraint_table)
+					ereport(ERROR,
+							(errcode(ERRCODE_SYNTAX_ERROR),
+							 errmsg("RAISE option already specified: %s",
+									"CONSTRAINT_TABLE")));
+				constraint_table = pstrdup(extval);
+				break;
+			case PLPGSQL_RAISEOPTION_CONSTRAINT_SCHEMA:
+				if (constraint_schema)
+					ereport(ERROR,
+							(errcode(ERRCODE_SYNTAX_ERROR),
+							 errmsg("RAISE option already specified: %s",
+									"CONSTRAINT_SCHEMA")));
+				constraint_schema = pstrdup(extval);
+				break;
+			case PLPGSQL_RAISEOPTION_ROUTINE_NAME:
+				if (routine_name)
+					ereport(ERROR,
+							(errcode(ERRCODE_SYNTAX_ERROR),
+							 errmsg("RAISE option already specified: %s",
+									"ROUTINE_NAME")));
+				routine_name = pstrdup(extval);
+				break;
+			case PLPGSQL_RAISEOPTION_ROUTINE_SCHEMA:
+				if (routine_schema)
+					ereport(ERROR,
+							(errcode(ERRCODE_SYNTAX_ERROR),
+							 errmsg("RAISE option already specified: %s",
+									"ROUTINE_SCHEMA")));
+				routine_schema = pstrdup(extval);
+				break;
+			case PLPGSQL_RAISEOPTION_TRIGGER_NAME:
+				if (trigger_name)
+					ereport(ERROR,
+							(errcode(ERRCODE_SYNTAX_ERROR),
+							 errmsg("RAISE option already specified: %s",
+									"TRIGGER_NAME")));
+				trigger_name = pstrdup(extval);
+				break;
+			case PLPGSQL_RAISEOPTION_TRIGGER_TABLE:
+				if (trigger_table)
+					ereport(ERROR,
+							(errcode(ERRCODE_SYNTAX_ERROR),
+							 errmsg("RAISE option already specified: %s",
+									"TRIGGER_TABLE")));
+				trigger_table = pstrdup(extval);
+				break;
+			case PLPGSQL_RAISEOPTION_TRIGGER_SCHEMA:
+				if (trigger_schema)
+					ereport(ERROR,
+							(errcode(ERRCODE_SYNTAX_ERROR),
+							 errmsg("RAISE option already specified: %s",
+									"TRIGGER_SCHEMA")));
+				trigger_schema = pstrdup(extval);
+				break;
 			default:
 				elog(ERROR, "unrecognized raise option: %d", opt->opt_type);
 		}
@@ -2837,7 +3010,18 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
 			(err_code ? errcode(err_code) : 0,
 			 errmsg_internal("%s", err_message),
 			 (err_detail != NULL) ? errdetail_internal("%s", err_detail) : 0,
-			 (err_hint != NULL) ? errhint("%s", err_hint) : 0));
+			 (err_hint != NULL) ? errhint("%s", err_hint) : 0,
+			 (column_name != NULL) ? erritem(PG_DIAG_COLUMN_NAME, column_name) : 0,
+			 (table_name != NULL) ? erritem(PG_DIAG_TABLE_NAME, table_name) : 0,
+			 (schema_name != NULL) ? erritem(PG_DIAG_SCHEMA_NAME, schema_name) : 0,
+			 (constraint_name != NULL) ? erritem(PG_DIAG_CONSTRAINT_NAME, constraint_name) : 0,
+			 (constraint_table != NULL) ? erritem(PG_DIAG_CONSTRAINT_TABLE, constraint_table) : 0,
+			 (constraint_schema != NULL) ? erritem(PG_DIAG_CONSTRAINT_SCHEMA, constraint_schema) : 0,
+			 (routine_name != NULL) ? erritem(PG_DIAG_ROUTINE_NAME, routine_name) : 0,
+			 (routine_schema != NULL) ? erritem(PG_DIAG_ROUTINE_SCHEMA, routine_schema) : 0,
+			 (trigger_name != NULL) ? erritem(PG_DIAG_TRIGGER_NAME, trigger_name) : 0,
+			 (trigger_table != NULL) ? erritem(PG_DIAG_TRIGGER_TABLE, trigger_table) : 0,
+			 (trigger_schema != NULL) ? erritem(PG_DIAG_TRIGGER_SCHEMA, trigger_schema) : 0));
 
 	estate->err_text = NULL;	/* un-suppress... */
 
@@ -2849,6 +3033,28 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
 		pfree(err_detail);
 	if (err_hint != NULL)
 		pfree(err_hint);
+	if (column_name != NULL)
+		pfree(column_name);
+	if (table_name != NULL)
+		pfree(table_name);
+	if (schema_name != NULL)
+		pfree(schema_name);
+	if (constraint_name != NULL)
+		pfree(constraint_name);
+	if (constraint_table != NULL)
+		pfree(constraint_table);
+	if (constraint_schema != NULL)
+		pfree(constraint_schema);
+	if (routine_name != NULL)
+		pfree(routine_name);
+	if (routine_schema != NULL)
+		pfree(routine_schema);
+	if (trigger_name != NULL)
+		pfree(trigger_name);
+	if (trigger_table != NULL)
+		pfree(trigger_table);
+	if (trigger_schema != NULL)
+		pfree(trigger_schema);
 
 	return PLPGSQL_RC_OK;
 }
@@ -2930,6 +3136,9 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
 		if ((*plugin_ptr)->func_setup)
 			((*plugin_ptr)->func_setup) (estate, func);
 	}
+
+	estate->tg_name = NULL;
+	estate->tg_schema = NULL;
 }
 
 /* ----------
diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c
index b5a72dd..b76bdca 100644
--- a/src/pl/plpgsql/src/pl_funcs.c
+++ b/src/pl/plpgsql/src/pl_funcs.c
@@ -287,6 +287,28 @@ plpgsql_getdiag_kindname(int kind)
 			return "RETURNED_SQLSTATE";
 		case PLPGSQL_GETDIAG_MESSAGE_TEXT:
 			return "MESSAGE_TEXT";
+		case PLPGSQL_GETDIAG_COLUMN_NAME:
+			return "COLUMN_NAME";
+		case PLPGSQL_GETDIAG_TABLE_NAME:
+			return "TABLE_NAME";
+		case PLPGSQL_GETDIAG_SCHEMA_NAME:
+			return "SCHEMA_NAME";
+		case PLPGSQL_GETDIAG_CONSTRAINT_NAME:
+			return "CONSTRAINT_NAME";
+		case PLPGSQL_GETDIAG_CONSTRAINT_TABLE:
+			return "CONSTRAINT_TABLE";
+		case PLPGSQL_GETDIAG_CONSTRAINT_SCHEMA:
+			return "CONSTRAINT_SCHEMA";
+		case PLPGSQL_GETDIAG_ROUTINE_NAME:
+			return "ROUTINE_NAME";
+		case PLPGSQL_GETDIAG_ROUTINE_SCHEMA:
+			return "ROUTINE_SCHEMA";
+		case PLPGSQL_GETDIAG_TRIGGER_NAME:
+			return "TRIGGER_NAME";
+		case PLPGSQL_GETDIAG_TRIGGER_TABLE:
+			return "TRIGGER_TABLE";
+		case PLPGSQL_GETDIAG_TRIGGER_SCHEMA:
+			return "TRIGGER_SCHEMA";
 	}
 
 	return "unknown";
@@ -1317,6 +1339,39 @@ dump_raise(PLpgSQL_stmt_raise *stmt)
 				case PLPGSQL_RAISEOPTION_HINT:
 					printf("    HINT = ");
 					break;
+				case PLPGSQL_RAISEOPTION_COLUMN_NAME:
+					printf("    COLUMN_NAME = ");
+					break;
+				case PLPGSQL_RAISEOPTION_TABLE_NAME:
+					printf("    TABLE_NAME = ");
+					break;
+				case PLPGSQL_RAISEOPTION_SCHEMA_NAME:
+					printf("    SCHEMA_NAME = ");
+					break;
+				case PLPGSQL_RAISEOPTION_CONSTRAINT_NAME:
+					printf("    CONSTRAINT_NAME = ");
+					break;
+				case PLPGSQL_RAISEOPTION_CONSTRAINT_TABLE:
+					printf("    CONSTRAINT_TABLE = ");
+					break;
+				case PLPGSQL_RAISEOPTION_CONSTRAINT_SCHEMA:
+					printf("    CONSTRAINT_SCHEMA = ");
+					break;
+				case PLPGSQL_RAISEOPTION_ROUTINE_NAME:
+					printf("    ROUTINE_NAME = ");
+					break;
+				case PLPGSQL_RAISEOPTION_ROUTINE_SCHEMA:
+					printf("    ROUTINE_SCHEMA = ");
+					break;
+				case PLPGSQL_RAISEOPTION_TRIGGER_NAME:
+					printf("    TRIGGER_NAME = ");
+					break;
+				case PLPGSQL_RAISEOPTION_TRIGGER_TABLE:
+					printf("    TRIGGER_TABLE = ");
+					break;
+				case PLPGSQL_RAISEOPTION_TRIGGER_SCHEMA:
+					printf("    TRIGGER_SCHEMA = ");
+					break;
 			}
 			dump_expr(opt->expr);
 			printf("\n");
diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c
index ebce3fd..600f294 100644
--- a/src/pl/plpgsql/src/pl_scanner.c
+++ b/src/pl/plpgsql/src/pl_scanner.c
@@ -109,7 +109,11 @@ static const ScanKeyword unreserved_keywords[] = {
 	PG_KEYWORD("alias", K_ALIAS, UNRESERVED_KEYWORD)
 	PG_KEYWORD("array", K_ARRAY, UNRESERVED_KEYWORD)
 	PG_KEYWORD("backward", K_BACKWARD, UNRESERVED_KEYWORD)
+	PG_KEYWORD("column_name", K_DIAG_COLUMN_NAME, UNRESERVED_KEYWORD)
 	PG_KEYWORD("constant", K_CONSTANT, UNRESERVED_KEYWORD)
+	PG_KEYWORD("constraint_name", K_DIAG_CONSTRAINT_NAME, UNRESERVED_KEYWORD)
+	PG_KEYWORD("constraint_schema", K_DIAG_CONSTRAINT_SCHEMA, UNRESERVED_KEYWORD)
+	PG_KEYWORD("constraint_table", K_DIAG_CONSTRAINT_TABLE, UNRESERVED_KEYWORD)
 	PG_KEYWORD("current", K_CURRENT, UNRESERVED_KEYWORD)
 	PG_KEYWORD("cursor", K_CURSOR, UNRESERVED_KEYWORD)
 	PG_KEYWORD("debug", K_DEBUG, UNRESERVED_KEYWORD)
@@ -139,12 +143,19 @@ static const ScanKeyword unreserved_keywords[] = {
 	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("routine_name", K_DIAG_ROUTINE_NAME, UNRESERVED_KEYWORD)
+	PG_KEYWORD("routine_schema", K_DIAG_ROUTINE_SCHEMA, UNRESERVED_KEYWORD)
 	PG_KEYWORD("row_count", K_ROW_COUNT, UNRESERVED_KEYWORD)
 	PG_KEYWORD("rowtype", K_ROWTYPE, UNRESERVED_KEYWORD)
+	PG_KEYWORD("schema_name", K_DIAG_SCHEMA_NAME, 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("table_name", K_DIAG_TABLE_NAME, UNRESERVED_KEYWORD)
+	PG_KEYWORD("trigger_name", K_DIAG_TRIGGER_NAME, UNRESERVED_KEYWORD)
+	PG_KEYWORD("trigger_schema", K_DIAG_TRIGGER_SCHEMA, UNRESERVED_KEYWORD)
+	PG_KEYWORD("trigger_table", K_DIAG_TRIGGER_TABLE, 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)
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index b63f336..cac98e1 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -127,7 +127,18 @@ enum
 	PLPGSQL_GETDIAG_ERROR_DETAIL,
 	PLPGSQL_GETDIAG_ERROR_HINT,
 	PLPGSQL_GETDIAG_RETURNED_SQLSTATE,
-	PLPGSQL_GETDIAG_MESSAGE_TEXT
+	PLPGSQL_GETDIAG_MESSAGE_TEXT,
+	PLPGSQL_GETDIAG_COLUMN_NAME,
+	PLPGSQL_GETDIAG_TABLE_NAME,
+	PLPGSQL_GETDIAG_SCHEMA_NAME,
+	PLPGSQL_GETDIAG_CONSTRAINT_NAME,
+	PLPGSQL_GETDIAG_CONSTRAINT_TABLE,
+	PLPGSQL_GETDIAG_CONSTRAINT_SCHEMA,
+	PLPGSQL_GETDIAG_ROUTINE_NAME,
+	PLPGSQL_GETDIAG_ROUTINE_SCHEMA,
+	PLPGSQL_GETDIAG_TRIGGER_NAME,
+	PLPGSQL_GETDIAG_TRIGGER_TABLE,
+	PLPGSQL_GETDIAG_TRIGGER_SCHEMA
 };
 
 /* --------
@@ -139,7 +150,18 @@ enum
 	PLPGSQL_RAISEOPTION_ERRCODE,
 	PLPGSQL_RAISEOPTION_MESSAGE,
 	PLPGSQL_RAISEOPTION_DETAIL,
-	PLPGSQL_RAISEOPTION_HINT
+	PLPGSQL_RAISEOPTION_HINT,
+	PLPGSQL_RAISEOPTION_COLUMN_NAME,
+	PLPGSQL_RAISEOPTION_TABLE_NAME,
+	PLPGSQL_RAISEOPTION_SCHEMA_NAME,
+	PLPGSQL_RAISEOPTION_CONSTRAINT_NAME,
+	PLPGSQL_RAISEOPTION_CONSTRAINT_TABLE,
+	PLPGSQL_RAISEOPTION_CONSTRAINT_SCHEMA,
+	PLPGSQL_RAISEOPTION_ROUTINE_NAME,
+	PLPGSQL_RAISEOPTION_ROUTINE_SCHEMA,
+	PLPGSQL_RAISEOPTION_TRIGGER_NAME,
+	PLPGSQL_RAISEOPTION_TRIGGER_TABLE,
+	PLPGSQL_RAISEOPTION_TRIGGER_SCHEMA
 };
 
 /* --------
@@ -679,6 +701,8 @@ typedef struct PLpgSQL_func_hashkey
 typedef struct PLpgSQL_function
 {								/* Complete compiled function	  */
 	char	   *fn_signature;
+	char	   *fn_name;
+	char	   *fn_schema;
 	Oid			fn_oid;
 	TransactionId fn_xmin;
 	ItemPointerData fn_tid;
@@ -765,6 +789,10 @@ typedef struct PLpgSQL_execstate
 	const char *err_text;		/* additional state info */
 
 	void	   *plugin_info;	/* reserved for use by optional plugin */
+
+	/* trigger description */
+	char	*tg_name;
+	char	*tg_schema;
 } PLpgSQL_execstate;
 
 
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index fb47fa7..92f1d67 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -4719,3 +4719,68 @@ ERROR:  value for domain orderedarray violates check constraint "sorted"
 CONTEXT:  PL/pgSQL function testoa(integer,integer,integer) line 5 at assignment
 drop function arrayassign1();
 drop function testoa(x1 int, x2 int, x3 int);
+create function test_eelog_outer()
+returns void as $$
+declare
+  column_name text;
+  table_name text;
+  schema_name text;
+  constraint_name text;
+  constraint_table text;
+  constraint_schema text;
+  routine_name text;
+  routine_schema text;
+  trigger_name text;
+  trigger_table text;
+  trigger_schema text;
+begin
+  perform test_eelog_inner();
+exception when others then
+  get stacked diagnostics
+    column_name = column_name,
+    table_name = table_name,
+    schema_name = schema_name,
+    constraint_name = constraint_name,
+    constraint_table = constraint_table,
+    constraint_schema = constraint_schema,
+    routine_name = routine_name,
+    routine_schema = routine_schema,
+    trigger_name = trigger_name,
+    trigger_table = trigger_table,
+    trigger_schema = trigger_schema;
+
+  raise notice 'column name: "%":, table name: "%", schema name: "%"', column_name, table_name, schema_name;
+  raise notice 'constraint name: "%", constraint table: %, constraint schema: "%"', constraint_name, constraint_table, constraint_schema;
+  raise notice 'routine name: "%", routine schema: "%"', routine_name, routine_schema;
+  raise notice 'trigger name: "%", trigger table: %, trigger schema: "%"', trigger_name, trigger_table, trigger_schema;
+end;
+$$ language plpgsql;
+create function test_eelog_inner()
+returns void as $$
+begin
+  raise exception 'custom exception'
+    using column_name = 'some_column_name',
+          table_name = 'some_table_name',
+          schema_name = 'some_schema_name',
+          constraint_name = 'some_constraint_name',
+          constraint_table = 'some_constraint_table',
+          constraint_schema = 'some_constraint_schema',
+          routine_name = 'some_routine',
+          routine_schema = 'some_routine_schema',
+          trigger_name = 'some_trigger_name',
+          trigger_table = 'some_trigger_table',
+          trigger_schema = 'some_trigger_schema';
+end;
+$$ language plpgsql;
+select test_eelog_outer();
+NOTICE:  column name: "some_column_name":, table name: "some_table_name", schema name: "some_schema_name"
+NOTICE:  constraint name: "some_constraint_name", constraint table: some_constraint_table, constraint schema: "some_constraint_schema"
+NOTICE:  routine name: "some_routine", routine schema: "some_routine_schema"
+NOTICE:  trigger name: "some_trigger_name", trigger table: some_trigger_table, trigger schema: "some_trigger_schema"
+ test_eelog_outer 
+------------------
+ 
+(1 row)
+
+drop function test_eelog_outer();
+drop function test_eelog_inner();
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
index 2b60b67..96a3d2e 100644
--- a/src/test/regress/sql/plpgsql.sql
+++ b/src/test/regress/sql/plpgsql.sql
@@ -3723,3 +3723,63 @@ select testoa(1,2,1); -- fail at update
 
 drop function arrayassign1();
 drop function testoa(x1 int, x2 int, x3 int);
+
+create function test_eelog_outer()
+returns void as $$
+declare
+  column_name text;
+  table_name text;
+  schema_name text;
+  constraint_name text;
+  constraint_table text;
+  constraint_schema text;
+  routine_name text;
+  routine_schema text;
+  trigger_name text;
+  trigger_table text;
+  trigger_schema text;
+begin
+  perform test_eelog_inner();
+exception when others then
+  get stacked diagnostics
+    column_name = column_name,
+    table_name = table_name,
+    schema_name = schema_name,
+    constraint_name = constraint_name,
+    constraint_table = constraint_table,
+    constraint_schema = constraint_schema,
+    routine_name = routine_name,
+    routine_schema = routine_schema,
+    trigger_name = trigger_name,
+    trigger_table = trigger_table,
+    trigger_schema = trigger_schema;
+
+  raise notice 'column name: "%":, table name: "%", schema name: "%"', column_name, table_name, schema_name;
+  raise notice 'constraint name: "%", constraint table: %, constraint schema: "%"', constraint_name, constraint_table, constraint_schema;
+  raise notice 'routine name: "%", routine schema: "%"', routine_name, routine_schema;
+  raise notice 'trigger name: "%", trigger table: %, trigger schema: "%"', trigger_name, trigger_table, trigger_schema;
+end;
+$$ language plpgsql;
+
+create function test_eelog_inner()
+returns void as $$
+begin
+  raise exception 'custom exception'
+    using column_name = 'some_column_name',
+          table_name = 'some_table_name',
+          schema_name = 'some_schema_name',
+          constraint_name = 'some_constraint_name',
+          constraint_table = 'some_constraint_table',
+          constraint_schema = 'some_constraint_schema',
+          routine_name = 'some_routine',
+          routine_schema = 'some_routine_schema',
+          trigger_name = 'some_trigger_name',
+          trigger_table = 'some_trigger_table',
+          trigger_schema = 'some_trigger_schema';
+end;
+$$ language plpgsql;
+
+select test_eelog_outer();
+
+drop function test_eelog_outer();
+drop function test_eelog_inner();