eelog5.patch

application/octet-stream

Filename: eelog5.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: context
File+
doc/src/sgml/config.sgml 13 0
doc/src/sgml/errcodes.sgml 12 0
doc/src/sgml/generate-errcodes-table.pl 25 0
doc/src/sgml/protocol.sgml 90 0
src/backend/access/nbtree/nbtinsert.c 7 0
src/backend/commands/tablecmds.c 16 0
src/backend/commands/typecmds.c 13 0
src/backend/executor/execMain.c 8 0
src/backend/executor/execQual.c 38 0
src/backend/executor/execUtils.c 6 0
src/backend/utils/adt/domains.c 10 0
src/backend/utils/adt/ri_triggers.c 13 0
src/backend/utils/errcodes.txt 15 0
src/backend/utils/error/elog.c 277 0
src/backend/utils/error/Makefile 1 0
src/backend/utils/generate-errcodes.pl 6 0
src/backend/utils/sort/tuplesort.c 8 0
src/include/postgres_ext.h 8 0
src/include/utils/elog.h 10 0
src/include/utils/rel.h 5 0
src/interfaces/libpq/fe-protocol3.c 33 0
src/pl/plpgsql/src/generate-plerrcodes.pl 2 0
src/pl/plpgsql/src/pl_comp.c 2 0
src/pl/plpgsql/src/pl_exec.c 20 0
src/pl/plpgsql/src/plpgsql.h 3 0
*** a/doc/src/sgml/config.sgml
--- b/doc/src/sgml/config.sgml
***************
*** 4246,4255 **** FROM pg_stat_activity;
          error context,
          user query that led to the error (if any and enabled by
          <varname>log_min_error_statement</>),
!         character count of the error position therein,
!         location of the error in the PostgreSQL source code
!         (if <varname>log_error_verbosity</> is set to <literal>verbose</>),
!         and application name.
          Here is a sample table definition for storing CSV-format log output:
  
  <programlisting>
--- 4246,4256 ----
          error context,
          user query that led to the error (if any and enabled by
          <varname>log_min_error_statement</>),
!         character count of the error position therein, and, if
!         <varname>log_error_verbosity</> is set to <literal>verbose</>,
!         column name, table name, constraint name and location of the
!         error in the PostgreSQL source code.
!         Application name is also listed.
          Here is a sample table definition for storing CSV-format log output:
  
  <programlisting>
***************
*** 4277,4282 **** CREATE TABLE postgres_log
--- 4278,4291 ----
    query text,
    query_pos integer,
    location text,
+   column_name text,
+   table_name text,
+   constraint_name text,
+   schema_name text,
+   routine_name text,
+   routine_schema text,
+   routine_oid oid,
+   trigger_name text,
    application_name text,
    PRIMARY KEY (session_id, session_line_num)
  );
*** a/doc/src/sgml/errcodes.sgml
--- b/doc/src/sgml/errcodes.sgml
***************
*** 42,47 ****
--- 42,59 ----
   </para>
  
   <para>
+    A small number of error codes listed in
+    <xref linkend="errcodes-table"> are guaranteed to provide
+    additional fields to facilitate applications in recognizing
+    particular domain-specific errors, provided that the the errors are
+    emitted from within the PostgreSQL server itself (it is not
+    strictly guaranteed that an application will not emit an
+    ill-considered error without these field, but with an errorcode
+    documented as providing them).  These additional fields are not
+    described by the SQL standard.
+  </para>
+ 
+  <para>
    The symbol shown in the column <quote>Condition Name</quote> is also
    the condition name to use in <application>PL/pgSQL</>.  Condition
    names can be written in either upper or lower case.  (Note that
*** a/doc/src/sgml/generate-errcodes-table.pl
--- b/doc/src/sgml/generate-errcodes-table.pl
***************
*** 41,46 **** while (<$errcodes>)
--- 41,71 ----
  		next;
  	}
  
+ 	# Emit requirement headers
+ 	if (/^Requirement:/)
+ 	{
+ 		# Replace the Requirement: string
+ 		s/^Requirement: /Provides: /;
+ 
+ 		# Replace "unused"
+ 		if (!s/unused/\(no fields\)/)
+ 		{
+ 			# Use literal for field names if appropriate
+ 			s/: (.*)/: <symbol>$1<\/symbol>/;
+ 		}
+ 
+ 		# Escape dashes for SGML
+ 		s/-/&mdash;/;
+ 
+ 		print "\n\n";
+ 		print "<row>\n";
+ 		print "<entry spanname=\"span12\">";
+ 		print "$_</entry>\n";
+ 		print "</row>\n";
+ 
+ 		next;
+ 	}
+ 
  	die unless /^([^\s]{5})\s+([EWS])\s+([^\s]+)(?:\s+)?([^\s]+)?/;
  
  	(my $sqlstate, my $type, my $errcode_macro, my $condition_name) =
*** a/doc/src/sgml/protocol.sgml
--- b/doc/src/sgml/protocol.sgml
***************
*** 4786,4791 **** message.
--- 4786,4881 ----
  </listitem>
  </varlistentry>
  
+ <varlistentry>
+ <term>
+ <literal>c</>
+ </term>
+ <listitem>
+ <para>
+         Column name: the name of the column associated with the the
+         error, if any.  This must be a column within the table of the
+         Table name field.
+ </para>
+ </listitem>
+ </varlistentry>
+ 
+ <varlistentry>
+ <term>
+ <literal>t</>
+ </term>
+ <listitem>
+ <para>
+         Table name: the name of the table associated with the error,
+         if any.
+ </para>
+ </listitem>
+ </varlistentry>
+ 
+ <varlistentry>
+ <term>
+ <literal>n</>
+ </term>
+ <listitem>
+ <para>
+         Constraint name: the name of the constraint associated with
+         the error, if any.  Note that NOT NULL constraints are not
+         cataloged, and as such will never have a constraint name,
+         though in the case of NOT NULL constraints on columns the
+         column name will be available.  This value is not guaranteed
+         to uniquely identify the constraint.  Even when combined with
+         other fields, constraint name will not unambiguously identify
+         the constraint in all cases, since it is possible for the
+         constraint to be in a different schema to the table associated
+         with the error, or for the constraint to not be associated
+         with a table at all.
+ </para>
+ </listitem>
+ </varlistentry>
+ 
+ <varlistentry>
+ <term>
+ <literal>r</>
+ </term>
+ <listitem>
+ <para>
+         Routine name: the name of routine that reports error.
+ </para>
+ </listitem>
+ </varlistentry>
+ 
+ <varlistentry>
+ <term>
+ <literal>u</>
+ </term>
+ <listitem>
+ <para>
+         Routine schema: the schema of routine that reports error.
+ </para>
+ </listitem>
+ </varlistentry>
+ 
+ <varlistentry>
+ <term>
+ <literal>O</>
+ </term>
+ <listitem>
+ <para>
+         Routine oid: the object id of routine that reports error.
+ </para>
+ </listitem>
+ </varlistentry>
+ 
+ <varlistentry>
+ <term>
+ <literal>T</>
+ </term>
+ <listitem>
+ <para>
+         Trigger name: the name of trigger that caused error.
+ </para>
+ </listitem>
+ </varlistentry>
+ 
  </variablelist>
  
  <para>
*** a/src/backend/access/nbtree/nbtinsert.c
--- b/src/backend/access/nbtree/nbtinsert.c
***************
*** 393,399 **** _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
  										RelationGetRelationName(rel)),
  								 errdetail("Key %s already exists.",
  										   BuildIndexValueDescription(rel,
! 														  values, isnull))));
  					}
  				}
  				else if (all_dead)
--- 393,401 ----
  										RelationGetRelationName(rel)),
  								 errdetail("Key %s already exists.",
  										   BuildIndexValueDescription(rel,
! 															values, isnull)),
! 								 errtable(heapRel),
! 								 errconstraint(RelationGetRelationName(rel))));
  					}
  				}
  				else if (all_dead)
***************
*** 455,461 **** _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
  				(errcode(ERRCODE_INTERNAL_ERROR),
  				 errmsg("failed to re-find tuple within index \"%s\"",
  						RelationGetRelationName(rel)),
! 		errhint("This may be because of a non-immutable index expression.")));
  
  	if (nbuf != InvalidBuffer)
  		_bt_relbuf(rel, nbuf);
--- 457,464 ----
  				(errcode(ERRCODE_INTERNAL_ERROR),
  				 errmsg("failed to re-find tuple within index \"%s\"",
  						RelationGetRelationName(rel)),
! 		 errhint("This may be because of a non-immutable index expression."),
! 				 errtable(heapRel)));
  
  	if (nbuf != InvalidBuffer)
  		_bt_relbuf(rel, nbuf);
***************
*** 533,539 **** _bt_findinsertloc(Relation rel,
  				   RelationGetRelationName(rel)),
  		errhint("Values larger than 1/3 of a buffer page cannot be indexed.\n"
  				"Consider a function index of an MD5 hash of the value, "
! 				"or use full text indexing.")));
  
  	/*----------
  	 * If we will need to split the page to put the item on this page,
--- 536,543 ----
  				   RelationGetRelationName(rel)),
  		errhint("Values larger than 1/3 of a buffer page cannot be indexed.\n"
  				"Consider a function index of an MD5 hash of the value, "
! 				"or use full text indexing."),
! 				 errtable(heapRel)));
  
  	/*----------
  	 * If we will need to split the page to put the item on this page,
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 3819,3828 **** ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
  				int			attn = lfirst_int(l);
  
  				if (heap_attisnull(tuple, attn + 1))
  					ereport(ERROR,
  							(errcode(ERRCODE_NOT_NULL_VIOLATION),
  							 errmsg("column \"%s\" contains null values",
! 								NameStr(newTupDesc->attrs[attn]->attname))));
  			}
  
  			foreach(l, tab->constraints)
--- 3819,3835 ----
  				int			attn = lfirst_int(l);
  
  				if (heap_attisnull(tuple, attn + 1))
+ 				{
+ 					Form_pg_attribute att = newTupDesc->attrs[attn];
+ 
  					ereport(ERROR,
  							(errcode(ERRCODE_NOT_NULL_VIOLATION),
  							 errmsg("column \"%s\" contains null values",
! 									NameStr(att->attname)),
! 							 (newrel) ?
! 							 errtablecol(newrel, NameStr(att->attname)) :
! 							 errtablecol(oldrel, NameStr(att->attname))));
! 				}
  			}
  
  			foreach(l, tab->constraints)
***************
*** 3836,3842 **** ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
  							ereport(ERROR,
  									(errcode(ERRCODE_CHECK_VIOLATION),
  									 errmsg("check constraint \"%s\" is violated by some row",
! 											con->name)));
  						break;
  					case CONSTR_FOREIGN:
  						/* Nothing to do here */
--- 3843,3853 ----
  							ereport(ERROR,
  									(errcode(ERRCODE_CHECK_VIOLATION),
  									 errmsg("check constraint \"%s\" is violated by some row",
! 											con->name),
! 									 (newrel) ?
! 									 errtable(newrel) :
! 									 errtable(oldrel),
! 									 errconstraint(con->name)));
  						break;
  					case CONSTR_FOREIGN:
  						/* Nothing to do here */
***************
*** 6653,6659 **** validateCheckConstraint(Relation rel, HeapTuple constrtup)
  			ereport(ERROR,
  					(errcode(ERRCODE_CHECK_VIOLATION),
  					 errmsg("check constraint \"%s\" is violated by some row",
! 							NameStr(constrForm->conname))));
  
  		ResetExprContext(econtext);
  	}
--- 6664,6672 ----
  			ereport(ERROR,
  					(errcode(ERRCODE_CHECK_VIOLATION),
  					 errmsg("check constraint \"%s\" is violated by some row",
! 							NameStr(constrForm->conname)),
! 					 errtable(rel),
! 					 errconstraint(NameStr(constrForm->conname))));
  
  		ResetExprContext(econtext);
  	}
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
***************
*** 98,104 **** static Oid	findTypeAnalyzeFunction(List *procname, Oid typeOid);
  static Oid	findRangeSubOpclass(List *opcname, Oid subtype);
  static Oid	findRangeCanonicalFunction(List *procname, Oid typeOid);
  static Oid	findRangeSubtypeDiffFunction(List *procname, Oid subtype);
! static void validateDomainConstraint(Oid domainoid, char *ccbin);
  static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
  static void checkEnumOwner(HeapTuple tup);
  static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
--- 98,104 ----
  static Oid	findRangeSubOpclass(List *opcname, Oid subtype);
  static Oid	findRangeCanonicalFunction(List *procname, Oid typeOid);
  static Oid	findRangeSubtypeDiffFunction(List *procname, Oid subtype);
! static void validateDomainConstraint(Oid domainoid, char *ccbin, char *conname);
  static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
  static void checkEnumOwner(HeapTuple tup);
  static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
***************
*** 2258,2270 **** AlterDomainNotNull(List *names, bool notNull)
  				for (i = 0; i < rtc->natts; i++)
  				{
  					int			attnum = rtc->atts[i];
  
  					if (heap_attisnull(tuple, attnum))
  						ereport(ERROR,
  								(errcode(ERRCODE_NOT_NULL_VIOLATION),
  								 errmsg("column \"%s\" of table \"%s\" contains null values",
! 								NameStr(tupdesc->attrs[attnum - 1]->attname),
! 										RelationGetRelationName(testrel))));
  				}
  			}
  			heap_endscan(scan);
--- 2258,2271 ----
  				for (i = 0; i < rtc->natts; i++)
  				{
  					int			attnum = rtc->atts[i];
+ 					Form_pg_attribute att = tupdesc->attrs[attnum - 1];
  
  					if (heap_attisnull(tuple, attnum))
  						ereport(ERROR,
  								(errcode(ERRCODE_NOT_NULL_VIOLATION),
  								 errmsg("column \"%s\" of table \"%s\" contains null values",
! 										NameStr(att->attname), RelationGetRelationName(testrel)),
! 								 errtablecol(testrel, NameStr(att->attname))));
  				}
  			}
  			heap_endscan(scan);
***************
*** 2470,2476 **** AlterDomainAddConstraint(List *names, Node *newConstraint)
  	 * attributes based on the domain the constraint is being added to.
  	 */
  	if (!constr->skip_validation)
! 		validateDomainConstraint(domainoid, ccbin);
  
  	/* Clean up */
  	heap_close(typrel, RowExclusiveLock);
--- 2471,2477 ----
  	 * attributes based on the domain the constraint is being added to.
  	 */
  	if (!constr->skip_validation)
! 		validateDomainConstraint(domainoid, ccbin, constr->conname);
  
  	/* Clean up */
  	heap_close(typrel, RowExclusiveLock);
***************
*** 2555,2561 **** AlterDomainValidateConstraint(List *names, char *constrName)
  			 HeapTupleGetOid(tuple));
  	conbin = TextDatumGetCString(val);
  
! 	validateDomainConstraint(domainoid, conbin);
  
  	/*
  	 * Now update the catalog, while we have the door open.
--- 2556,2562 ----
  			 HeapTupleGetOid(tuple));
  	conbin = TextDatumGetCString(val);
  
! 	validateDomainConstraint(domainoid, conbin, constrName);
  
  	/*
  	 * Now update the catalog, while we have the door open.
***************
*** 2576,2582 **** AlterDomainValidateConstraint(List *names, char *constrName)
  }
  
  static void
! validateDomainConstraint(Oid domainoid, char *ccbin)
  {
  	Expr	   *expr = (Expr *) stringToNode(ccbin);
  	List	   *rels;
--- 2577,2583 ----
  }
  
  static void
! validateDomainConstraint(Oid domainoid, char *ccbin, char *conname)
  {
  	Expr	   *expr = (Expr *) stringToNode(ccbin);
  	List	   *rels;
***************
*** 2618,2623 **** validateDomainConstraint(Oid domainoid, char *ccbin)
--- 2619,2625 ----
  				Datum		d;
  				bool		isNull;
  				Datum		conResult;
+ 				const char *col = NameStr(tupdesc->attrs[attnum - 1]->attname);
  
  				d = heap_getattr(tuple, attnum, tupdesc, &isNull);
  
***************
*** 2632,2639 **** validateDomainConstraint(Oid domainoid, char *ccbin)
  					ereport(ERROR,
  							(errcode(ERRCODE_CHECK_VIOLATION),
  							 errmsg("column \"%s\" of table \"%s\" contains values that violate the new constraint",
! 								NameStr(tupdesc->attrs[attnum - 1]->attname),
! 									RelationGetRelationName(testrel))));
  			}
  
  			ResetExprContext(econtext);
--- 2634,2644 ----
  					ereport(ERROR,
  							(errcode(ERRCODE_CHECK_VIOLATION),
  							 errmsg("column \"%s\" of table \"%s\" contains values that violate the new constraint",
! 									col,
! 									RelationGetRelationName(testrel)),
! 							 errtablecol(testrel, col),
! 							 conname?
! 							 errconstraint(conname):0));
  			}
  
  			ResetExprContext(econtext);
*** a/src/backend/executor/execMain.c
--- b/src/backend/executor/execMain.c
***************
*** 1522,1535 **** ExecConstraints(ResultRelInfo *resultRelInfo,
  
  		for (attrChk = 1; attrChk <= natts; attrChk++)
  		{
  			if (rel->rd_att->attrs[attrChk - 1]->attnotnull &&
  				slot_attisnull(slot, attrChk))
  				ereport(ERROR,
  						(errcode(ERRCODE_NOT_NULL_VIOLATION),
  						 errmsg("null value in column \"%s\" violates not-null constraint",
! 						  NameStr(rel->rd_att->attrs[attrChk - 1]->attname)),
  						 errdetail("Failing row contains %s.",
! 								   ExecBuildSlotValueDescription(slot, 64))));
  		}
  	}
  
--- 1522,1538 ----
  
  		for (attrChk = 1; attrChk <= natts; attrChk++)
  		{
+ 			Form_pg_attribute Chk = rel->rd_att->attrs[attrChk - 1];
+ 
  			if (rel->rd_att->attrs[attrChk - 1]->attnotnull &&
  				slot_attisnull(slot, attrChk))
  				ereport(ERROR,
  						(errcode(ERRCODE_NOT_NULL_VIOLATION),
  						 errmsg("null value in column \"%s\" violates not-null constraint",
! 								NameStr(Chk->attname)),
  						 errdetail("Failing row contains %s.",
! 								   ExecBuildSlotValueDescription(slot, 64)),
! 						 errtablecol(rel, NameStr(Chk->attname))));
  		}
  	}
  
***************
*** 1543,1549 **** ExecConstraints(ResultRelInfo *resultRelInfo,
  					 errmsg("new row for relation \"%s\" violates check constraint \"%s\"",
  							RelationGetRelationName(rel), failed),
  					 errdetail("Failing row contains %s.",
! 							   ExecBuildSlotValueDescription(slot, 64))));
  	}
  }
  
--- 1546,1554 ----
  					 errmsg("new row for relation \"%s\" violates check constraint \"%s\"",
  							RelationGetRelationName(rel), failed),
  					 errdetail("Failing row contains %s.",
! 							   ExecBuildSlotValueDescription(slot, 64)),
! 					 errtable(rel),
! 					 errconstraint(failed)));
  	}
  }
  
*** a/src/backend/executor/execQual.c
--- b/src/backend/executor/execQual.c
***************
*** 54,59 ****
--- 54,60 ----
  #include "utils/builtins.h"
  #include "utils/lsyscache.h"
  #include "utils/memutils.h"
+ #include "utils/rel.h"
  #include "utils/typcache.h"
  #include "utils/xml.h"
  
***************
*** 3864,3869 **** ExecEvalCoerceToDomain(CoerceToDomainState *cstate, ExprContext *econtext,
--- 3865,3876 ----
  	CoerceToDomain *ctest = (CoerceToDomain *) cstate->xprstate.expr;
  	Datum		result;
  	ListCell   *l;
+ 	Relation	heapRel = NULL;
+ 	struct EState *estate = econtext->ecxt_estate;
+ 
+ 	/* Build relation description, if possible */
+ 	if (estate && estate->es_result_relation_info)
+ 		heapRel = estate->es_result_relation_info->ri_RelationDesc;
  
  	result = ExecEvalExpr(cstate->arg, econtext, isNull, isDone);
  
***************
*** 3878,3887 **** ExecEvalCoerceToDomain(CoerceToDomainState *cstate, ExprContext *econtext,
  		{
  			case DOM_CONSTRAINT_NOTNULL:
  				if (*isNull)
! 					ereport(ERROR,
! 							(errcode(ERRCODE_NOT_NULL_VIOLATION),
! 							 errmsg("domain %s does not allow null values",
! 									format_type_be(ctest->resulttype))));
  				break;
  			case DOM_CONSTRAINT_CHECK:
  				{
--- 3885,3904 ----
  		{
  			case DOM_CONSTRAINT_NOTNULL:
  				if (*isNull)
! 				{
! 					if (heapRel)
! 						ereport(ERROR,
! 								(errcode(ERRCODE_NOT_NULL_VIOLATION),
! 								 errmsg("domain %s does not allow null values",
! 										format_type_be(ctest->resulttype)),
! 								 errtable(heapRel)));
! 
! 					else
! 						ereport(ERROR,
! 								(errcode(ERRCODE_NOT_NULL_VIOLATION),
! 								 errmsg("domain %s does not allow null values",
! 										format_type_be(ctest->resulttype))));
! 				}
  				break;
  			case DOM_CONSTRAINT_CHECK:
  				{
***************
*** 3907,3917 **** ExecEvalCoerceToDomain(CoerceToDomainState *cstate, ExprContext *econtext,
  
  					if (!conIsNull &&
  						!DatumGetBool(conResult))
! 						ereport(ERROR,
! 								(errcode(ERRCODE_CHECK_VIOLATION),
! 								 errmsg("value for domain %s violates check constraint \"%s\"",
! 										format_type_be(ctest->resulttype),
! 										con->name)));
  					econtext->domainValue_datum = save_datum;
  					econtext->domainValue_isNull = save_isNull;
  
--- 3924,3946 ----
  
  					if (!conIsNull &&
  						!DatumGetBool(conResult))
! 					{
! 						if (heapRel)
! 							ereport(ERROR,
! 									(errcode(ERRCODE_CHECK_VIOLATION),
! 									 errmsg("value for domain %s violates check constraint \"%s\"",
! 											format_type_be(ctest->resulttype),
! 											con->name),
! 									 errtable(heapRel),
! 									 errconstraint(con->name)));
! 						else
! 							ereport(ERROR,
! 									(errcode(ERRCODE_CHECK_VIOLATION),
! 									 errmsg("value for domain %s violates check constraint \"%s\"",
! 											format_type_be(ctest->resulttype),
! 											con->name),
! 									 errconstraint(con->name)));
! 					}
  					econtext->domainValue_datum = save_datum;
  					econtext->domainValue_isNull = save_isNull;
  
*** a/src/backend/executor/execUtils.c
--- b/src/backend/executor/execUtils.c
***************
*** 1307,1320 **** retry:
  					 errmsg("could not create exclusion constraint \"%s\"",
  							RelationGetRelationName(index)),
  					 errdetail("Key %s conflicts with key %s.",
! 							   error_new, error_existing)));
  		else
  			ereport(ERROR,
  					(errcode(ERRCODE_EXCLUSION_VIOLATION),
  					 errmsg("conflicting key value violates exclusion constraint \"%s\"",
  							RelationGetRelationName(index)),
  					 errdetail("Key %s conflicts with existing key %s.",
! 							   error_new, error_existing)));
  	}
  
  	index_endscan(index_scan);
--- 1307,1324 ----
  					 errmsg("could not create exclusion constraint \"%s\"",
  							RelationGetRelationName(index)),
  					 errdetail("Key %s conflicts with key %s.",
! 							   error_new, error_existing),
! 					 errtable(heap),
! 					 errconstraint(RelationGetRelationName(index))));
  		else
  			ereport(ERROR,
  					(errcode(ERRCODE_EXCLUSION_VIOLATION),
  					 errmsg("conflicting key value violates exclusion constraint \"%s\"",
  							RelationGetRelationName(index)),
  					 errdetail("Key %s conflicts with existing key %s.",
! 							   error_new, error_existing),
! 					 errtable(heap),
! 					 errconstraint(RelationGetRelationName(index))));
  	}
  
  	index_endscan(index_scan);
*** a/src/backend/utils/adt/domains.c
--- b/src/backend/utils/adt/domains.c
***************
*** 36,41 ****
--- 36,42 ----
  #include "lib/stringinfo.h"
  #include "utils/builtins.h"
  #include "utils/lsyscache.h"
+ #include "utils/rel.h"
  
  
  /*
***************
*** 123,132 **** domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
--- 124,140 ----
  		{
  			case DOM_CONSTRAINT_NOTNULL:
  				if (isnull)
+ 				{
+ 					/*
+ 					 * We don't provide errtable here, because it is
+ 					 * unavailable, though we require it anywhere where it is
+ 					 * available in principle.
+ 					 */
  					ereport(ERROR,
  							(errcode(ERRCODE_NOT_NULL_VIOLATION),
  							 errmsg("domain %s does not allow null values",
  									format_type_be(my_extra->domain_type))));
+ 				}
  				break;
  			case DOM_CONSTRAINT_CHECK:
  				{
***************
*** 163,169 **** domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
  								(errcode(ERRCODE_CHECK_VIOLATION),
  								 errmsg("value for domain %s violates check constraint \"%s\"",
  										format_type_be(my_extra->domain_type),
! 										con->name)));
  					break;
  				}
  			default:
--- 171,178 ----
  								(errcode(ERRCODE_CHECK_VIOLATION),
  								 errmsg("value for domain %s violates check constraint \"%s\"",
  										format_type_be(my_extra->domain_type),
! 										con->name),
! 								 errconstraint(con->name)));
  					break;
  				}
  			default:
*** a/src/backend/utils/adt/ri_triggers.c
--- b/src/backend/utils/adt/ri_triggers.c
***************
*** 339,345 **** RI_FKey_check(TriggerData *trigdata)
  							 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
  							  RelationGetRelationName(trigdata->tg_relation),
  									NameStr(riinfo->conname)),
! 							 errdetail("MATCH FULL does not allow mixing of null and nonnull key values.")));
  					heap_close(pk_rel, RowShareLock);
  					return PointerGetDatum(NULL);
  
--- 339,347 ----
  							 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
  							  RelationGetRelationName(trigdata->tg_relation),
  									NameStr(riinfo->conname)),
! 							 errdetail("MATCH FULL does not allow mixing of null and nonnull key values."),
! 							 errtable(trigdata->tg_relation),
! 							 errconstraint(NameStr(riinfo->conname))));
  					heap_close(pk_rel, RowShareLock);
  					return PointerGetDatum(NULL);
  
***************
*** 2467,2473 **** RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
  					 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
  							RelationGetRelationName(fk_rel),
  							NameStr(fake_riinfo.conname)),
! 					 errdetail("MATCH FULL does not allow mixing of null and nonnull key values.")));
  
  		/*
  		 * We tell ri_ReportViolation we were doing the RI_PLAN_CHECK_LOOKUPPK
--- 2469,2478 ----
  					 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
  							RelationGetRelationName(fk_rel),
  							NameStr(fake_riinfo.conname)),
! 					 errdetail("MATCH FULL does not allow mixing of null and nonnull key values."),
! 					 errtable(fk_rel),
! 					 errconstraint(NameStr(fake_riinfo.conname))));
! 
  
  		/*
  		 * We tell ri_ReportViolation we were doing the RI_PLAN_CHECK_LOOKUPPK
***************
*** 3219,3225 **** ri_ReportViolation(const RI_ConstraintInfo *riinfo,
  						NameStr(riinfo->conname)),
  				 errdetail("Key (%s)=(%s) is not present in table \"%s\".",
  						   key_names.data, key_values.data,
! 						   RelationGetRelationName(pk_rel))));
  	else
  		ereport(ERROR,
  				(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
--- 3224,3232 ----
  						NameStr(riinfo->conname)),
  				 errdetail("Key (%s)=(%s) is not present in table \"%s\".",
  						   key_names.data, key_values.data,
! 						   RelationGetRelationName(pk_rel)),
! 				 errtable(fk_rel),
! 				 errconstraint(NameStr(riinfo->conname))));
  	else
  		ereport(ERROR,
  				(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
***************
*** 3229,3235 **** ri_ReportViolation(const RI_ConstraintInfo *riinfo,
  						RelationGetRelationName(fk_rel)),
  			errdetail("Key (%s)=(%s) is still referenced from table \"%s\".",
  					  key_names.data, key_values.data,
! 					  RelationGetRelationName(fk_rel))));
  }
  
  
--- 3236,3244 ----
  						RelationGetRelationName(fk_rel)),
  			errdetail("Key (%s)=(%s) is still referenced from table \"%s\".",
  					  key_names.data, key_values.data,
! 					  RelationGetRelationName(fk_rel)),
! 				 errtable(pk_rel),
! 				 errconstraint(NameStr(riinfo->conname))));
  }
  
  
*** a/src/backend/utils/errcodes.txt
--- b/src/backend/utils/errcodes.txt
***************
*** 200,213 **** Section: Class 22 - Data Exception
  2200S    E    ERRCODE_INVALID_XML_COMMENT                                    invalid_xml_comment
  2200T    E    ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION                     invalid_xml_processing_instruction
  
  Section: Class 23 - Integrity Constraint Violation
! 
  23000    E    ERRCODE_INTEGRITY_CONSTRAINT_VIOLATION                         integrity_constraint_violation
  23001    E    ERRCODE_RESTRICT_VIOLATION                                     restrict_violation
  23502    E    ERRCODE_NOT_NULL_VIOLATION                                     not_null_violation
  23503    E    ERRCODE_FOREIGN_KEY_VIOLATION                                  foreign_key_violation
  23505    E    ERRCODE_UNIQUE_VIOLATION                                       unique_violation
  23514    E    ERRCODE_CHECK_VIOLATION                                        check_violation
  23P01    E    ERRCODE_EXCLUSION_VIOLATION                                    exclusion_violation
  
  Section: Class 24 - Invalid Cursor State
--- 200,227 ----
  2200S    E    ERRCODE_INVALID_XML_COMMENT                                    invalid_xml_comment
  2200T    E    ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION                     invalid_xml_processing_instruction
  
+ # Postgres coding standards mandate that certain fields be available in all
+ # instances for some of the Class 23 errcodes, documented under "Requirement: "
+ # here.  Some other errcode's ereport sites may, at their own discretion, make
+ # errcolumn, errtable, errconstraint and errschema fields available too.
+ # Furthermore, it is possible to make some fields available beyond those
+ # formally required at callsites involving these Class 23 errcodes with
+ # "Requirements: ".
  Section: Class 23 - Integrity Constraint Violation
! Requirement: unused
  23000    E    ERRCODE_INTEGRITY_CONSTRAINT_VIOLATION                         integrity_constraint_violation
+ Requirement: unused
  23001    E    ERRCODE_RESTRICT_VIOLATION                                     restrict_violation
+ # Note that requirements for ERRCODE_NOT_NULL do not apply to domains:
+ Requirement: schema_name, table_name
  23502    E    ERRCODE_NOT_NULL_VIOLATION                                     not_null_violation
+ Requirement: schema_name, table_name, constraint_name
  23503    E    ERRCODE_FOREIGN_KEY_VIOLATION                                  foreign_key_violation
+ Requirement: schema_name, table_name, constraint_name
  23505    E    ERRCODE_UNIQUE_VIOLATION                                       unique_violation
+ Requirement: constraint_name
  23514    E    ERRCODE_CHECK_VIOLATION                                        check_violation
+ Requirement: schema_name, table_name, constraint_name
  23P01    E    ERRCODE_EXCLUSION_VIOLATION                                    exclusion_violation
  
  Section: Class 24 - Invalid Cursor State
*** a/src/backend/utils/error/Makefile
--- b/src/backend/utils/error/Makefile
***************
*** 12,17 **** subdir = src/backend/utils/error
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = assert.o elog.o
  
  include $(top_srcdir)/src/backend/common.mk
--- 12,17 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = assert.o elog.o relerror.o
  
  include $(top_srcdir)/src/backend/common.mk
*** a/src/backend/utils/error/elog.c
--- b/src/backend/utils/error/elog.c
***************
*** 130,135 **** static void write_syslog(int level, const char *line);
--- 130,136 ----
  #endif
  
  static void write_console(const char *line, int len);
+ static void set_errdata_field(char **ptr, const char *str);
  
  #ifdef WIN32
  extern char *event_source;
***************
*** 477,482 **** errfinish(int dummy,...)
--- 478,497 ----
  		pfree(edata->context);
  	if (edata->internalquery)
  		pfree(edata->internalquery);
+ 	if (edata->column_name)
+ 		pfree(edata->column_name);
+ 	if (edata->table_name)
+ 		pfree(edata->table_name);
+ 	if (edata->schema_name)
+ 		pfree(edata->schema_name);
+ 	if (edata->constraint_name)
+ 		pfree(edata->constraint_name);
+ 	if (edata->routine_name)
+ 		pfree(edata->routine_name);
+ 	if (edata->routine_schema)
+ 		pfree(edata->routine_schema);
+ 	if (edata->trigger_name)
+ 		pfree(edata->trigger_name);
  
  	errordata_stack_depth--;
  
***************
*** 1101,1106 **** internalerrquery(const char *query)
--- 1116,1214 ----
  }
  
  /*
+  * errroutineid -- set routine_oid of top custom functions
+  */
+ int
+ errroutineid(Oid routineid)
+ {
+ 	ErrorData  *edata = &errordata[errordata_stack_depth];
+ 
+ 	/* we don't bother incrementing recursion_depth */
+ 	CHECK_STACK_DEPTH();
+ 
+ 	edata->routine_oid = routineid;
+ 
+ 	return 0;					/* return value does not matter */
+ }
+ 
+ /*
+  * err_generic_string -- used to set individual ErrorData string fields.
+  *
+  * Callers should prefer higher-level abstractions such as those within
+  * relerror.c.
+  */
+ int
+ err_generic_string(int field, const char *str)
+ {
+ 	ErrorData  *edata = &errordata[errordata_stack_depth];
+ 
+ 	/* we don't bother incrementing recursion_depth */
+ 	CHECK_STACK_DEPTH();
+ 
+ 	switch (field)
+ 	{
+ 		case PG_DIAG_MESSAGE_PRIMARY:
+ 			set_errdata_field(&edata->message, str);
+ 			break;
+ 
+ 		case PG_DIAG_MESSAGE_DETAIL:
+ 			set_errdata_field(&edata->detail, str);
+ 			break;
+ 
+ 		case PG_DIAG_MESSAGE_HINT:
+ 			set_errdata_field(&edata->hint, str);
+ 			break;
+ 
+ 		case PG_DIAG_CONTEXT:
+ 			set_errdata_field(&edata->context, str);
+ 			break;
+ 
+ 		case PG_DIAG_COLUMN_NAME:
+ 			set_errdata_field(&edata->column_name, str);
+ 			break;
+ 
+ 		case PG_DIAG_TABLE_NAME:
+ 			set_errdata_field(&edata->table_name, str);
+ 			break;
+ 
+ 		case PG_DIAG_SCHEMA_NAME:
+ 			set_errdata_field(&edata->schema_name, str);
+ 			break;
+ 
+ 		case PG_DIAG_CONSTRAINT_NAME:
+ 			set_errdata_field(&edata->constraint_name, str);
+ 			break;
+ 
+ 		case PG_DIAG_ROUTINE_NAME:
+ 			set_errdata_field(&edata->routine_name, str);
+ 			break;
+ 
+ 		case PG_DIAG_ROUTINE_SCHEMA:
+ 			set_errdata_field(&edata->routine_schema, str);
+ 			break;
+ 
+ 		case PG_DIAG_TRIGGER_NAME:
+ 			set_errdata_field(&edata->trigger_name, str);
+ 			break;
+ 
+ 		default:
+ 			elog(ERROR, "unsupported or unknown ErrorData field id: %d", field);
+ 	}
+ 
+ 	return 0;			/* return value does not matter */
+ }
+ 
+ /*
+  * set_errdata_field --- set an ErrorData string field
+  */
+ static void
+ set_errdata_field(char **ptr, const char *str)
+ {
+ 	Assert(*ptr == NULL);
+ 	*ptr = MemoryContextStrdup(ErrorContext, str);
+ }
+ 
+ /*
   * geterrcode --- return the currently set SQLSTATE error code
   *
   * This is only intended for use in error callback subroutines, since there
***************
*** 1374,1379 **** CopyErrorData(void)
--- 1482,1501 ----
  		newedata->context = pstrdup(newedata->context);
  	if (newedata->internalquery)
  		newedata->internalquery = pstrdup(newedata->internalquery);
+ 	if (newedata->column_name)
+ 		newedata->column_name = pstrdup(newedata->column_name);
+ 	if (newedata->table_name)
+ 		newedata->table_name = pstrdup(newedata->table_name);
+ 	if (newedata->schema_name)
+ 		newedata->schema_name = pstrdup(newedata->schema_name);
+ 	if (newedata->constraint_name)
+ 		newedata->constraint_name = pstrdup(newedata->constraint_name);
+ 	if (newedata->routine_name)
+ 		newedata->routine_name = pstrdup(newedata->routine_name);
+ 	if (newedata->routine_schema)
+ 		newedata->routine_schema = pstrdup(newedata->routine_schema);
+ 	if (newedata->trigger_name)
+ 		newedata->trigger_name = pstrdup(newedata->trigger_name);
  
  	return newedata;
  }
***************
*** 1399,1404 **** FreeErrorData(ErrorData *edata)
--- 1521,1541 ----
  		pfree(edata->context);
  	if (edata->internalquery)
  		pfree(edata->internalquery);
+ 	if (edata->column_name)
+ 		pfree(edata->column_name);
+ 	if (edata->table_name)
+ 		pfree(edata->table_name);
+ 	if (edata->schema_name)
+ 		pfree(edata->schema_name);
+ 	if (edata->constraint_name)
+ 		pfree(edata->constraint_name);
+ 	if (edata->routine_name)
+ 		pfree(edata->routine_name);
+ 	if (edata->routine_schema)
+ 		pfree(edata->routine_schema);
+ 	if (edata->trigger_name)
+ 		pfree(edata->trigger_name);
+ 
  	pfree(edata);
  }
  
***************
*** 1471,1476 **** ReThrowError(ErrorData *edata)
--- 1608,1627 ----
  		newedata->context = pstrdup(newedata->context);
  	if (newedata->internalquery)
  		newedata->internalquery = pstrdup(newedata->internalquery);
+ 	if (newedata->column_name)
+ 		newedata->column_name = pstrdup(newedata->column_name);
+ 	if (newedata->table_name)
+ 		newedata->table_name = pstrdup(newedata->table_name);
+ 	if (newedata->schema_name)
+ 		newedata->schema_name = pstrdup(newedata->schema_name);
+ 	if (newedata->constraint_name)
+ 		newedata->constraint_name = pstrdup(newedata->constraint_name);
+ 	if (newedata->routine_name)
+ 		newedata->routine_name = pstrdup(newedata->routine_name);
+ 	if (newedata->routine_schema)
+ 		newedata->routine_schema = pstrdup(newedata->routine_schema);
+ 	if (newedata->trigger_name)
+ 		newedata->trigger_name = pstrdup(newedata->trigger_name);
  
  	recursion_depth--;
  	PG_RE_THROW();
***************
*** 2275,2282 **** write_csvlog(ErrorData *edata)
  							 edata->filename, edata->lineno);
  		appendCSVLiteral(&buf, msgbuf.data);
  		pfree(msgbuf.data);
  	}
- 	appendStringInfoChar(&buf, ',');
  
  	/* application name */
  	if (application_name)
--- 2426,2457 ----
  							 edata->filename, edata->lineno);
  		appendCSVLiteral(&buf, msgbuf.data);
  		pfree(msgbuf.data);
+ 		appendStringInfoChar(&buf, ',');
+ 
+ 		appendCSVLiteral(&buf, edata->column_name);
+ 		appendStringInfoChar(&buf, ',');
+ 
+ 		appendCSVLiteral(&buf, edata->table_name);
+ 		appendStringInfoChar(&buf, ',');
+ 
+ 		appendCSVLiteral(&buf, edata->schema_name);
+ 		appendStringInfoChar(&buf, ',');
+ 
+ 		appendCSVLiteral(&buf, edata->constraint_name);
+ 		appendStringInfoChar(&buf, ',');
+ 
+ 		appendCSVLiteral(&buf, edata->routine_name);
+ 		appendStringInfoChar(&buf, ',');
+ 
+ 		appendCSVLiteral(&buf, edata->routine_schema);
+ 		appendStringInfoChar(&buf, ',');
+ 
+ 		appendStringInfo(&buf, "%d", edata->routine_oid);
+ 		appendStringInfoChar(&buf, ',');
+ 
+ 		appendCSVLiteral(&buf, edata->trigger_name);
+ 		appendStringInfoChar(&buf, ',');
  	}
  
  	/* application name */
  	if (application_name)
***************
*** 2385,2391 **** send_message_to_server_log(ErrorData *edata)
  		}
  		if (Log_error_verbosity >= PGERROR_VERBOSE)
  		{
! 			/* assume no newlines in funcname or filename... */
  			if (edata->funcname && edata->filename)
  			{
  				log_line_prefix(&buf, edata);
--- 2560,2569 ----
  		}
  		if (Log_error_verbosity >= PGERROR_VERBOSE)
  		{
! 			/*
! 			 * assume no newlines in funcname, filename, column_name,
! 			 * table_name, constraint_name or schema_name...
! 			 */
  			if (edata->funcname && edata->filename)
  			{
  				log_line_prefix(&buf, edata);
***************
*** 2399,2404 **** send_message_to_server_log(ErrorData *edata)
--- 2577,2630 ----
  				appendStringInfo(&buf, _("LOCATION:  %s:%d\n"),
  								 edata->filename, edata->lineno);
  			}
+ 			if (edata->column_name && edata->table_name)
+ 			{
+ 				log_line_prefix(&buf, edata);
+ 				appendStringInfo(&buf, _("COLUMN NAME:  %s:%s\n"),
+ 								 edata->table_name, edata->column_name);
+ 			}
+ 			else if (edata->table_name)
+ 			{
+ 				log_line_prefix(&buf, edata);
+ 				appendStringInfo(&buf, _("TABLE NAME:  %s\n"),
+ 								 edata->table_name);
+ 			}
+ 			if (edata->constraint_name)
+ 			{
+ 				log_line_prefix(&buf, edata);
+ 				appendStringInfo(&buf, _("CONSTRAINT NAME:  %s\n"),
+ 								 edata->constraint_name);
+ 			}
+ 			if (edata->schema_name)
+ 			{
+ 				log_line_prefix(&buf, edata);
+ 				appendStringInfo(&buf, _("SCHEMA NAME:  %s\n"),
+ 								 edata->schema_name);
+ 			}
+ 			if (edata->routine_name)
+ 			{
+ 				log_line_prefix(&buf, edata);
+ 				appendStringInfo(&buf, _("ROUTINE NAME:  %s\n"),
+ 								 edata->routine_name);
+ 			}
+ 			if (edata->routine_schema)
+ 			{
+ 				log_line_prefix(&buf, edata);
+ 				appendStringInfo(&buf, _("ROUTINE SCHEMA:  %s\n"),
+ 								 edata->routine_schema);
+ 			}
+ 			if (edata->routine_oid != InvalidOid)
+ 			{
+ 				log_line_prefix(&buf, edata);
+ 				appendStringInfo(&buf, _("ROUTINE OID:  %d\n"),
+ 								 edata->routine_oid);
+ 			}
+ 			if (edata->trigger_name)
+ 			{
+ 				log_line_prefix(&buf, edata);
+ 				appendStringInfo(&buf, _("TRIGGER NAME:  %s\n"),
+ 								 edata->trigger_name);
+ 			}
  		}
  	}
  
***************
*** 2695,2700 **** send_message_to_frontend(ErrorData *edata)
--- 2921,2975 ----
  			err_sendstring(&msgbuf, edata->funcname);
  		}
  
+ 		if (edata->column_name)
+ 		{
+ 			pq_sendbyte(&msgbuf, PG_DIAG_COLUMN_NAME);
+ 			err_sendstring(&msgbuf, edata->column_name);
+ 		}
+ 
+ 		if (edata->table_name)
+ 		{
+ 			pq_sendbyte(&msgbuf, PG_DIAG_TABLE_NAME);
+ 			err_sendstring(&msgbuf, edata->table_name);
+ 		}
+ 
+ 		if (edata->constraint_name)
+ 		{
+ 			pq_sendbyte(&msgbuf, PG_DIAG_CONSTRAINT_NAME);
+ 			err_sendstring(&msgbuf, edata->constraint_name);
+ 		}
+ 
+ 		if (edata->schema_name)
+ 		{
+ 			pq_sendbyte(&msgbuf, PG_DIAG_SCHEMA_NAME);
+ 			err_sendstring(&msgbuf, edata->schema_name);
+ 		}
+ 
+ 		if (edata->routine_name)
+ 		{
+ 			pq_sendbyte(&msgbuf, PG_DIAG_ROUTINE_NAME);
+ 			err_sendstring(&msgbuf, edata->routine_name);
+ 		}
+ 
+ 		if (edata->routine_schema)
+ 		{
+ 			pq_sendbyte(&msgbuf, PG_DIAG_ROUTINE_SCHEMA);
+ 			err_sendstring(&msgbuf, edata->routine_schema);
+ 		}
+ 
+ 		if (edata->routine_oid != InvalidOid)
+ 		{
+ 			snprintf(tbuf, sizeof(tbuf), "%d", edata->routine_oid);
+ 			pq_sendbyte(&msgbuf, PG_DIAG_ROUTINE_OID);
+ 			err_sendstring(&msgbuf, tbuf);
+ 		}
+ 
+ 		if (edata->trigger_name)
+ 		{
+ 			pq_sendbyte(&msgbuf, PG_DIAG_TRIGGER_NAME);
+ 			err_sendstring(&msgbuf, edata->trigger_name);
+ 		}
+ 
  		pq_sendbyte(&msgbuf, '\0');		/* terminator */
  	}
  	else
*** a/src/backend/utils/generate-errcodes.pl
--- b/src/backend/utils/generate-errcodes.pl
***************
*** 28,33 **** while (<$errcodes>)
--- 28,39 ----
  		print "\n/* $header */\n";
  		next;
  	}
+ 	elsif (/(^Requirement:.*)/)
+ 	{
+ 		my $header = $1;
+ 		print "/* $header */\n";
+ 		next;
+ 	}
  
  	die "unable to parse errcodes.txt"
  	  unless /^([^\s]{5})\s+[EWS]\s+([^\s]+)/;
*** a/src/backend/utils/sort/tuplesort.c
--- b/src/backend/utils/sort/tuplesort.c
***************
*** 3014,3019 **** comparetup_index_btree(const SortTuple *a, const SortTuple *b,
--- 3014,3020 ----
  	 * for equal keys at the end.
  	 */
  	ScanKey		scanKey = state->indexScanKey;
+ 	Relation	indexRel = state->indexRel;
  	IndexTuple	tuple1;
  	IndexTuple	tuple2;
  	int			keysz;
***************
*** 3038,3044 **** comparetup_index_btree(const SortTuple *a, const SortTuple *b,
  	tuple1 = (IndexTuple) a->tuple;
  	tuple2 = (IndexTuple) b->tuple;
  	keysz = state->nKeys;
! 	tupDes = RelationGetDescr(state->indexRel);
  	scanKey++;
  	for (nkey = 2; nkey <= keysz; nkey++, scanKey++)
  	{
--- 3039,3045 ----
  	tuple1 = (IndexTuple) a->tuple;
  	tuple2 = (IndexTuple) b->tuple;
  	keysz = state->nKeys;
! 	tupDes = RelationGetDescr(indexRel);
  	scanKey++;
  	for (nkey = 2; nkey <= keysz; nkey++, scanKey++)
  	{
***************
*** 3075,3080 **** comparetup_index_btree(const SortTuple *a, const SortTuple *b,
--- 3076,3083 ----
  	{
  		Datum		values[INDEX_MAX_KEYS];
  		bool		isnull[INDEX_MAX_KEYS];
+ 		const char *indrelname = RelationGetRelationName(indexRel);
+ 		Relation	heapRel = RelationIdGetRelation(indexRel->rd_index->indrelid);
  
  		/*
  		 * Some rather brain-dead implementations of qsort (such as the one in
***************
*** 3088,3097 **** comparetup_index_btree(const SortTuple *a, const SortTuple *b,
  		ereport(ERROR,
  				(errcode(ERRCODE_UNIQUE_VIOLATION),
  				 errmsg("could not create unique index \"%s\"",
! 						RelationGetRelationName(state->indexRel)),
  				 errdetail("Key %s is duplicated.",
! 						   BuildIndexValueDescription(state->indexRel,
! 													  values, isnull))));
  	}
  
  	/*
--- 3091,3101 ----
  		ereport(ERROR,
  				(errcode(ERRCODE_UNIQUE_VIOLATION),
  				 errmsg("could not create unique index \"%s\"",
! 						indrelname),
  				 errdetail("Key %s is duplicated.",
! 						   BuildIndexValueDescription(indexRel, values, isnull)),
! 				 errtable(heapRel),
! 				 errconstraint(indrelname)));
  	}
  
  	/*
*** a/src/include/postgres_ext.h
--- b/src/include/postgres_ext.h
***************
*** 60,64 **** typedef PG_INT64_TYPE pg_int64;
--- 60,72 ----
  #define PG_DIAG_SOURCE_FILE		'F'
  #define PG_DIAG_SOURCE_LINE		'L'
  #define PG_DIAG_SOURCE_FUNCTION 'R'
+ #define PG_DIAG_COLUMN_NAME			'c'
+ #define PG_DIAG_TABLE_NAME			't'
+ #define PG_DIAG_CONSTRAINT_NAME		'n'
+ #define PG_DIAG_SCHEMA_NAME			's'
+ #define PG_DIAG_ROUTINE_NAME		'r'
+ #define PG_DIAG_ROUTINE_SCHEMA		'u'
+ #define PG_DIAG_ROUTINE_OID		'O'
+ #define PG_DIAG_TRIGGER_NAME			'T'
  
  #endif   /* POSTGRES_EXT_H */
*** a/src/include/utils/elog.h
--- b/src/include/utils/elog.h
***************
*** 206,211 **** extern int	geterrcode(void);
--- 206,213 ----
  extern int	geterrposition(void);
  extern int	getinternalerrposition(void);
  
+ extern int	errroutineid(Oid routineid);
+ extern int	err_generic_string(int field, const char *str);
  
  /*----------
   * Old-style error reporting API: to be used in this way:
***************
*** 338,343 **** typedef struct ErrorData
--- 340,353 ----
  	char	   *detail_log;		/* detail error message for server log only */
  	char	   *hint;			/* hint message */
  	char	   *context;		/* context message */
+ 	char	   *column_name;	/* name of column */
+ 	char	   *table_name;		/* name of table */
+ 	char	   *constraint_name;	/* name of constraint */
+ 	char	   *schema_name;	/* name of schema */
+ 	char	   *routine_name;	/* name of routine */
+ 	char	   *routine_schema;	/* schema of routine */
+ 	Oid	   routine_oid;		/* oid of routine */
+ 	char	   *trigger_name;	/* name of trigger */
  	int			cursorpos;		/* cursor index into query string */
  	int			internalpos;	/* cursor index into internalquery */
  	char	   *internalquery;	/* text of internally-generated query */
*** a/src/include/utils/rel.h
--- b/src/include/utils/rel.h
***************
*** 398,401 **** typedef struct StdRdOptions
--- 398,406 ----
  extern void RelationIncrementReferenceCount(Relation rel);
  extern void RelationDecrementReferenceCount(Relation rel);
  
+ /* routines in utils/error/relerror.c */
+ extern int	errtablecol(Relation table, const char *colname);
+ extern int	errtable(Relation table);
+ extern int	errconstraint(const char *cname);
+ 
  #endif   /* REL_H */
*** a/src/interfaces/libpq/fe-protocol3.c
--- b/src/interfaces/libpq/fe-protocol3.c
***************
*** 936,941 **** pqGetErrorNotice3(PGconn *conn, bool isError)
--- 936,974 ----
  								  valf, vall);
  			appendPQExpBufferChar(&workBuf, '\n');
  		}
+ 
+ 		val = PQresultErrorField(res, PG_DIAG_COLUMN_NAME);
+ 		if (val)
+ 			appendPQExpBuffer(&workBuf,
+ 							  libpq_gettext("COLUMN NAME:  %s\n"), val);
+ 		val = PQresultErrorField(res, PG_DIAG_TABLE_NAME);
+ 		if (val)
+ 			appendPQExpBuffer(&workBuf,
+ 							  libpq_gettext("TABLE NAME:  %s\n"), val);
+ 		val = PQresultErrorField(res, PG_DIAG_CONSTRAINT_NAME);
+ 		if (val)
+ 			appendPQExpBuffer(&workBuf,
+ 							  libpq_gettext("CONSTRAINT NAME:  %s\n"), val);
+ 		val = PQresultErrorField(res, PG_DIAG_SCHEMA_NAME);
+ 		if (val)
+ 			appendPQExpBuffer(&workBuf,
+ 							  libpq_gettext("SCHEMA NAME:  %s\n"), val);
+ 		val = PQresultErrorField(res, PG_DIAG_ROUTINE_NAME);
+ 		if (val)
+ 			appendPQExpBuffer(&workBuf,
+ 							  libpq_gettext("ROUTINE NAME:  %s\n"), val);
+ 		val = PQresultErrorField(res, PG_DIAG_ROUTINE_SCHEMA);
+ 		if (val)
+ 			appendPQExpBuffer(&workBuf,
+ 							  libpq_gettext("ROUTINE SHEMA:  %s\n"), val);
+ 		val = PQresultErrorField(res, PG_DIAG_ROUTINE_OID);
+ 		if (val)
+ 			appendPQExpBuffer(&workBuf,
+ 							  libpq_gettext("ROUTINE OID:  %s\n"), val);
+ 		val = PQresultErrorField(res, PG_DIAG_TRIGGER_NAME);
+ 		if (val)
+ 			appendPQExpBuffer(&workBuf,
+ 							  libpq_gettext("TRIGGER NAME:  %s\n"), val);
  	}
  
  	/*
*** a/src/pl/plpgsql/src/generate-plerrcodes.pl
--- b/src/pl/plpgsql/src/generate-plerrcodes.pl
***************
*** 20,27 **** while (<$errcodes>)
  	next if /^#/;
  	next if /^\s*$/;
  
! 	# Skip section headers
! 	next if /^Section:/;
  
  	die unless /^([^\s]{5})\s+([EWS])\s+([^\s]+)(?:\s+)?([^\s]+)?/;
  
--- 20,27 ----
  	next if /^#/;
  	next if /^\s*$/;
  
! 	# Skip section headers, and requirement notes
! 	next if /^Section:/ or /^Requirement:/;
  
  	die unless /^([^\s]{5})\s+([EWS])\s+([^\s]+)(?:\s+)?([^\s]+)?/;
  
*** a/src/pl/plpgsql/src/pl_comp.c
--- b/src/pl/plpgsql/src/pl_comp.c
***************
*** 344,349 **** do_compile(FunctionCallInfo fcinfo,
--- 344,351 ----
  	compile_tmp_cxt = MemoryContextSwitchTo(func_cxt);
  
  	function->fn_signature = format_procedure(fcinfo->flinfo->fn_oid);
+ 	function->fn_name = get_func_name(fcinfo->flinfo->fn_oid);
+ 	function->fn_schema = get_namespace_name(get_func_namespace(fcinfo->flinfo->fn_oid));
  	function->fn_oid = fcinfo->flinfo->fn_oid;
  	function->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
  	function->fn_tid = procTup->t_self;
*** a/src/pl/plpgsql/src/pl_exec.c
--- b/src/pl/plpgsql/src/pl_exec.c
***************
*** 583,588 **** plpgsql_exec_trigger(PLpgSQL_function *func,
--- 583,590 ----
  	var->isnull = false;
  	var->freeval = true;
  
+ 	estate.trigger_name = trigdata->tg_trigger->tgname;
+ 
  	var = (PLpgSQL_var *) (estate.datums[func->tg_when_varno]);
  	if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
  		var->value = CStringGetTextDatum("BEFORE");
***************
*** 917,922 **** plpgsql_exec_error_callback(void *arg)
--- 919,941 ----
  	else
  		errcontext("PL/pgSQL function %s",
  				   estate->func->fn_signature);
+ 
+ 	/*
+ 	 * if execution of this function is on top of error_context_stack,
+ 	 * then set enhanced diagnostics fields related to function.
+ 	 */
+ 	if (error_context_stack->callback == plpgsql_exec_error_callback &&
+ 			(PLpgSQL_execstate *) error_context_stack->arg == estate)
+ 	{
+ 		PLpgSQL_function *func = estate->func;
+ 
+ 		errroutineid(func->fn_oid);
+ 		err_generic_string(PG_DIAG_ROUTINE_NAME, func->fn_name);
+ 		err_generic_string(PG_DIAG_ROUTINE_SCHEMA, func->fn_schema);
+ 
+ 		if (func->fn_is_trigger != PLPGSQL_NOT_TRIGGER)
+ 			err_generic_string(PG_DIAG_TRIGGER_NAME, estate->trigger_name);
+ 	}
  }
  
  
***************
*** 3053,3058 **** plpgsql_estate_setup(PLpgSQL_execstate *estate,
--- 3072,3078 ----
  
  	estate->err_stmt = NULL;
  	estate->err_text = NULL;
+ 	estate->trigger_name = NULL;
  
  	estate->plugin_info = NULL;
  
*** a/src/pl/plpgsql/src/plpgsql.h
--- b/src/pl/plpgsql/src/plpgsql.h
***************
*** 686,691 **** typedef enum PLpgSQL_trigtype
--- 686,693 ----
  typedef struct PLpgSQL_function
  {								/* Complete compiled function	  */
  	char	   *fn_signature;
+ 	char		*fn_schema;
+ 	char		*fn_name;				/* fields for enhanced diagnostics */
  	Oid			fn_oid;
  	TransactionId fn_xmin;
  	ItemPointerData fn_tid;
***************
*** 754,759 **** typedef struct PLpgSQL_execstate
--- 756,762 ----
  	char	   *exitlabel;		/* the "target" label of the current EXIT or
  								 * CONTINUE stmt, if any */
  	ErrorData  *cur_error;		/* current exception handler's error */
+ 	char	*trigger_name;		/* name of trigger for enhanced diagnostics */
  
  	Tuplestorestate *tuple_store;		/* SRFs accumulate results here */
  	MemoryContext tuple_store_cxt;