*** a/doc/src/sgml/config.sgml
--- b/doc/src/sgml/config.sgml
***************
*** 4258,4263 **** CREATE TABLE postgres_log
--- 4258,4273 ----
    query_pos integer,
    location text,
    application_name text,
+   column_name text,
+   table_name text,
+   schema_name text,
+   constraint_name text,
+   constraint_table text,
+   constraint_schema text,
+   routine_name text,
+   trigger_name text,
+   trigger_table text,
+   trigger_schema text,
    PRIMARY KEY (session_id, session_line_num)
  );
  </programlisting>
*** a/doc/src/sgml/protocol.sgml
--- b/doc/src/sgml/protocol.sgml
***************
*** 4722,4734 **** message.
  </listitem>
  </varlistentry>
  
  </variablelist>
  
  <para>
  The client is responsible for formatting displayed information to meet its
  needs; in particular it should break long lines as needed.  Newline characters
  appearing in the error message fields should be treated as paragraph breaks,
! not line breaks.
  </para>
  
  </sect1>
--- 4722,4867 ----
  </listitem>
  </varlistentry>
  
+ <varlistentry>
+ <term>
+ <literal>c</>
+ </term>
+ <listitem>
+ <para>
+         Column name: the name of the column associated with the the error, if
+         any.
+ </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>s</>
+ </term>
+ <listitem>
+ <para>
+         Schema name: the name of schema associated with the error.
+ </para>
+ </listitem>
+ </varlistentry>
+ 
+ <varlistentry>
+ <term>
+ <literal>n</>
+ </term>
+ <listitem>
+ <para>
+         Constraint name: the name of the constraint associated with the error,
+         if any.
+ </para>
+ </listitem>
+ </varlistentry>
+ 
+ <varlistentry>
+ <term>
+ <literal>o</>
+ </term>
+ <listitem>
+ <para>
+         Constraint table: the table name of the constraint associated with the
+         error, if any.
+ </para>
+ </listitem>
+ </varlistentry>
+ 
+ <varlistentry>
+ <term>
+ <literal>m</>
+ </term>
+ <listitem>
+ <para>
+         Constraint schema: the schema name of the constraint associated with the
+         error, if any.
+ </para>
+ </listitem>
+ </varlistentry>
+ 
+ <varlistentry>
+ <term>
+ <literal>r</>
+ </term>
+ <listitem>
+ <para>
+         Routine name: the name of the routine associated with the error, if any.
+ </para>
+ </listitem>
+ </varlistentry>
+ 
+ <varlistentry>
+ <term>
+ <literal>u</>
+ </term>
+ <listitem>
+ <para>
+         Routine schema: the schema of the routine associated with the error, if
+         any.
+ </para>
+ </listitem>
+ </varlistentry>
+ 
+ <varlistentry>
+ <term>
+ <literal>g</>
+ </term>
+ <listitem>
+ <para>
+         Trigger name: the name of the trigger associated with the error, if any.
+ </para>
+ </listitem>
+ </varlistentry>
+ 
+ <varlistentry>
+ <term>
+ <literal>i</>
+ </term>
+ <listitem>
+ <para>
+         Trigger table: the name of the table of the trigger associated with the
+         error, if any.
+ </para>
+ </listitem>
+ </varlistentry>
+ 
+ <varlistentry>
+ <term>
+ <literal>h</>
+ </term>
+ <listitem>
+ <para>
+         Trigger schema: the schema name of the trigger associated with the
+         error, if any.
+ </para>
+ </listitem>
+ </varlistentry>
+ 
  </variablelist>
  
  <para>
  The client is responsible for formatting displayed information to meet its
  needs; in particular it should break long lines as needed.  Newline characters
  appearing in the error message fields should be treated as paragraph breaks,
! not line breaks. Note that certain object names represented as ErrorResponse
! fields, such as Trigger name, are not guaranteed to be unique across a database,
! and such objects cannot generally be uniquely identified by name alone.  It may
! be necessary in these cases to unambiguously establish the identity of the
! object of interest based on a unique combination of the object's name, schema
! name, and, in the case of integrity constraints, table name.
  </para>
  
  </sect1>
*** 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,400 ----
  										RelationGetRelationName(rel)),
  								 errdetail("Key %s already exists.",
  										   BuildIndexValueDescription(rel,
! 														  values, isnull)),
! 								 errrel(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);
--- 456,463 ----
  				(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."),
! 				 errrel(rel)));
  
  	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,
--- 535,542 ----
  				   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."),
! 				errrel(rel)));
  
  	/*----------
  	 * 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
***************
*** 3805,3814 **** 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)
--- 3805,3821 ----
  				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) ?
! 								errrelcol(newrel, NameStr(att->attname)) :
! 									errrelcol(oldrel, NameStr(att->attname))));
! 				}
  			}
  
  			foreach(l, tab->constraints)
***************
*** 3822,3828 **** 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 */
--- 3829,3841 ----
  							ereport(ERROR,
  									(errcode(ERRCODE_CHECK_VIOLATION),
  									 errmsg("check constraint \"%s\" is violated by some row",
! 											con->name),
! 									 (newrel) ?
! 										errrel(newrel) :
! 											errrel(oldrel),
! 									 (newrel) ?
! 										errconstraint(newrel, con->name) :
! 											errconstraint(oldrel, con->name)));
  						break;
  					case CONSTR_FOREIGN:
  						/* Nothing to do here */
***************
*** 6620,6626 **** 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);
  	}
--- 6633,6641 ----
  			ereport(ERROR,
  					(errcode(ERRCODE_CHECK_VIOLATION),
  					 errmsg("check constraint \"%s\" is violated by some row",
! 							NameStr(constrForm->conname)),
! 					 errrel(rel),
! 					 errconstraint(rel, NameStr(constrForm->conname))));
  
  		ResetExprContext(econtext);
  	}
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
***************
*** 2235,2247 **** 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);
--- 2235,2248 ----
  				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)),
! 								 errrelcol(testrel, NameStr(att->attname))));
  				}
  			}
  			heap_endscan(scan);
***************
*** 2610,2616 **** validateDomainConstraint(Oid domainoid, char *ccbin)
  							(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);
--- 2611,2619 ----
  							(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)),
! 							errrelcol(testrel,
! 								NameStr(tupdesc->attrs[attnum - 1]->attname))));
  			}
  
  			ResetExprContext(econtext);
*** a/src/backend/executor/execMain.c
--- b/src/backend/executor/execMain.c
***************
*** 1516,1529 **** 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))));
  		}
  	}
  
--- 1516,1532 ----
  
  		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)),
! 						 errrelcol(rel, NameStr(Chk->attname))));
  		}
  	}
  
***************
*** 1537,1543 **** ExecConstraints(ResultRelInfo *resultRelInfo,
  					 errmsg("new row for relation \"%s\" violates check constraint \"%s\"",
  							RelationGetRelationName(rel), failed),
  					 errdetail("Failing row contains %s.",
! 							   ExecBuildSlotValueDescription(slot, 64))));
  	}
  }
  
--- 1540,1548 ----
  					 errmsg("new row for relation \"%s\" violates check constraint \"%s\"",
  							RelationGetRelationName(rel), failed),
  					 errdetail("Failing row contains %s.",
! 							ExecBuildSlotValueDescription(slot, 64)),
! 					 errrel(rel),
! 					 errconstraint(rel, failed)));
  	}
  }
  
*** a/src/backend/executor/execUtils.c
--- b/src/backend/executor/execUtils.c
***************
*** 1304,1317 **** 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);
--- 1304,1319 ----
  					 errmsg("could not create exclusion constraint \"%s\"",
  							RelationGetRelationName(index)),
  					 errdetail("Key %s conflicts with key %s.",
! 							   error_new, error_existing),
! 					 errrel(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),
! 					 errrel(index)));
  	}
  
  	index_endscan(index_scan);
*** 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."),
! 							 errrel(trigdata->tg_relation),
! 							 errconstraint(trigdata->tg_relation, 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."),
! 					 errrel(fk_rel),
! 					 errconstraint(fk_rel, 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)),
! 				 errrel(fk_rel),
! 				 errconstraint(fk_rel, NameStr(riinfo->conname))));
  	else
  		ereport(ERROR,
  				(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
***************
*** 3227,3235 **** ri_ReportViolation(const RI_ConstraintInfo *riinfo,
  						RelationGetRelationName(pk_rel),
  						NameStr(riinfo->conname),
  						RelationGetRelationName(fk_rel)),
! 			errdetail("Key (%s)=(%s) is still referenced from table \"%s\".",
! 					  key_names.data, key_values.data,
! 					  RelationGetRelationName(fk_rel))));
  }
  
  
--- 3234,3244 ----
  						RelationGetRelationName(pk_rel),
  						NameStr(riinfo->conname),
  						RelationGetRelationName(fk_rel)),
! 				 errdetail("Key (%s)=(%s) is still referenced from table \"%s\".",
! 						  key_names.data, key_values.data,
! 						  RelationGetRelationName(fk_rel)),
! 				 errrel(pk_rel),
! 				 errconstraint(fk_rel, NameStr(riinfo->conname))));
  }
  
  
*** 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
***************
*** 131,136 **** static void write_syslog(int level, const char *line);
--- 131,139 ----
  
  static void write_console(const char *line, int len);
  
+ static void set_errdata_field(char **ptr, const char *str, bool overwrite);
+ 
+ 
  #ifdef WIN32
  extern char *event_source;
  static void write_eventlog(int level, const char *line, int len);
***************
*** 477,482 **** errfinish(int dummy,...)
--- 480,507 ----
  		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->constraint_table)
+ 		pfree(edata->constraint_table);
+ 	if (edata->constraint_schema)
+ 		pfree(edata->constraint_schema);
+ 	if (edata->routine_name)
+ 		pfree(edata->routine_name);
+ 	if (edata->routine_schema)
+ 		pfree(edata->routine_schema);
+ 	if (edata->trigger_name)
+ 		pfree(edata->trigger_name);
+ 	if (edata->trigger_table)
+ 		pfree(edata->trigger_table);
+ 	if (edata->trigger_schema)
+ 		pfree(edata->trigger_schema);
  
  	errordata_stack_depth--;
  
***************
*** 1079,1084 **** internalerrquery(const char *query)
--- 1104,1218 ----
  }
  
  /*
+  * err_generic_string -- generic setting of ErrorData string fields
+  */
+ 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, true);
+ 			break;
+ 
+ 		case PG_DIAG_MESSAGE_DETAIL:
+ 			set_errdata_field(&edata->detail, str, true);
+ 			break;
+ 
+ 		case PG_DIAG_MESSAGE_HINT:
+ 			set_errdata_field(&edata->hint, str, true);
+ 			break;
+ 
+ 		case PG_DIAG_CONTEXT:
+ 			set_errdata_field(&edata->context, str, true);
+ 			break;
+ 
+ 		case PG_DIAG_COLUMN_NAME:
+ 			set_errdata_field(&edata->column_name, str, true);
+ 			break;
+ 
+ 		case PG_DIAG_TABLE_NAME:
+ 			set_errdata_field(&edata->table_name, str, true);
+ 			break;
+ 
+ 		case PG_DIAG_SCHEMA_NAME:
+ 			set_errdata_field(&edata->schema_name, str, true);
+ 			break;
+ 
+ 		case PG_DIAG_CONSTRAINT_NAME:
+ 			set_errdata_field(&edata->constraint_name, str, true);
+ 			break;
+ 
+ 		case PG_DIAG_CONSTRAINT_TABLE:
+ 			set_errdata_field(&edata->constraint_table, str, true);
+ 			break;
+ 
+ 		case PG_DIAG_CONSTRAINT_SCHEMA:
+ 			set_errdata_field(&edata->constraint_schema, str, true);
+ 			break;
+ 
+ 		/*
+ 		 * The remaining fields, once set, will not be set again. We need to
+ 		 * guard against resetting here because there is partial redundancy in
+ 		 * the fields set between some of our potential callers, even though two
+ 		 * or more of them might reasonably coexist within the same ereport
+ 		 * call.
+ 		 */
+ 		case PG_DIAG_ROUTINE_NAME:
+ 			set_errdata_field(&edata->routine_name, str, false);
+ 			break;
+ 
+ 		case PG_DIAG_ROUTINE_SCHEMA:
+ 			set_errdata_field(&edata->routine_schema, str, false);
+ 			break;
+ 
+ 		case PG_DIAG_TRIGGER_NAME:
+ 			set_errdata_field(&edata->trigger_name, str, false);
+ 			break;
+ 
+ 		case PG_DIAG_TRIGGER_TABLE:
+ 			set_errdata_field(&edata->trigger_table, str, false);
+ 			break;
+ 
+ 		case PG_DIAG_TRIGGER_SCHEMA:
+ 			set_errdata_field(&edata->trigger_schema, str, false);
+ 			break;
+ 
+ 		default:
+ 			elog(ERROR, "unknown ErrorData field id %d",
+ 						    field);
+ 	}
+ 
+ 	return 0;			/* return value does not matter */
+ }
+ 
+ /*
+  * set_errdata_field --- set an ErrorData string field, while potentially
+  * avoiding overwriting any existing value
+  */
+ static void
+ set_errdata_field(char **ptr, const char *str, bool overwrite)
+ {
+ 	if (*ptr != NULL)
+ 	{
+ 		/* Avoid overwriting existing value entirely */
+ 		if (!overwrite)
+ 			return;
+ 
+ 		pfree(*ptr);
+ 		*ptr = NULL;
+ 	}
+ 
+ 	if (str != 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
***************
*** 1352,1357 **** CopyErrorData(void)
--- 1486,1513 ----
  		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->constraint_table)
+ 		newedata->constraint_table = pstrdup(newedata->constraint_table);
+ 	if (newedata->constraint_schema)
+ 		newedata->constraint_schema = pstrdup(newedata->constraint_schema);
+ 	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);
+ 	if (newedata->trigger_table)
+ 		newedata->trigger_table = pstrdup(newedata->trigger_table);
+ 	if (newedata->trigger_schema)
+ 		newedata->trigger_schema = pstrdup(newedata->trigger_schema);
  
  	return newedata;
  }
***************
*** 1377,1382 **** FreeErrorData(ErrorData *edata)
--- 1533,1560 ----
  		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->constraint_table)
+ 		pfree(edata->constraint_table);
+ 	if (edata->constraint_schema)
+ 		pfree(edata->constraint_schema);
+ 	if (edata->routine_name)
+ 		pfree(edata->routine_name);
+ 	if (edata->routine_schema)
+ 		pfree(edata->routine_schema);
+ 	if (edata->trigger_name)
+ 		pfree(edata->trigger_name);
+ 	if (edata->trigger_table)
+ 		pfree(edata->trigger_table);
+ 	if (edata->trigger_schema)
+ 		pfree(edata->trigger_schema);
  	pfree(edata);
  }
  
***************
*** 1449,1454 **** ReThrowError(ErrorData *edata)
--- 1627,1654 ----
  		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->constraint_table)
+ 		newedata->constraint_table = pstrdup(newedata->constraint_table);
+ 	if (newedata->constraint_schema)
+ 		newedata->constraint_schema = pstrdup(newedata->constraint_schema);
+ 	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);
+ 	if (newedata->trigger_table)
+ 		newedata->trigger_table = pstrdup(newedata->trigger_table);
+ 	if (newedata->trigger_schema)
+ 		newedata->trigger_schema = pstrdup(newedata->trigger_schema);
  
  	recursion_depth--;
  	PG_RE_THROW();
***************
*** 2238,2244 **** write_csvlog(ErrorData *edata)
  	appendStringInfoChar(&buf, ',');
  
  	/* file error location */
- 	if (Log_error_verbosity >= PGERROR_VERBOSE)
  	{
  		StringInfoData msgbuf;
  
--- 2438,2443 ----
***************
*** 2259,2264 **** write_csvlog(ErrorData *edata)
--- 2458,2507 ----
  	/* application name */
  	if (application_name)
  		appendCSVLiteral(&buf, application_name);
+ 	appendStringInfoChar(&buf, ',');
+ 
+ 	if (Log_error_verbosity >= PGERROR_VERBOSE)
+ 		appendCSVLiteral(&buf, edata->column_name);
+ 	appendStringInfoChar(&buf, ',');
+ 
+ 	if (Log_error_verbosity >= PGERROR_VERBOSE)
+ 		appendCSVLiteral(&buf, edata->table_name);
+ 	appendStringInfoChar(&buf, ',');
+ 
+ 	if (Log_error_verbosity >= PGERROR_VERBOSE)
+ 		appendCSVLiteral(&buf, edata->schema_name);
+ 	appendStringInfoChar(&buf, ',');
+ 
+ 	if (Log_error_verbosity >= PGERROR_VERBOSE)
+ 		appendCSVLiteral(&buf, edata->constraint_name);
+ 	appendStringInfoChar(&buf, ',');
+ 
+ 	if (Log_error_verbosity >= PGERROR_VERBOSE)
+ 		appendCSVLiteral(&buf, edata->constraint_table);
+ 	appendStringInfoChar(&buf, ',');
+ 
+ 	if (Log_error_verbosity >= PGERROR_VERBOSE)
+ 		appendCSVLiteral(&buf, edata->constraint_schema);
+ 	appendStringInfoChar(&buf, ',');
+ 
+ 	if (Log_error_verbosity >= PGERROR_VERBOSE)
+ 		appendCSVLiteral(&buf, edata->routine_name);
+ 	appendStringInfoChar(&buf, ',');
+ 
+ 	if (Log_error_verbosity >= PGERROR_VERBOSE)
+ 		appendCSVLiteral(&buf, edata->routine_schema);
+ 	appendStringInfoChar(&buf, ',');
+ 
+ 	if (Log_error_verbosity >= PGERROR_VERBOSE)
+ 		appendCSVLiteral(&buf, edata->trigger_name);
+ 	appendStringInfoChar(&buf, ',');
+ 
+ 	if (Log_error_verbosity >= PGERROR_VERBOSE)
+ 		appendCSVLiteral(&buf, edata->trigger_table);
+ 	appendStringInfoChar(&buf, ',');
+ 
+ 	if (Log_error_verbosity >= PGERROR_VERBOSE)
+ 		appendCSVLiteral(&buf, edata->trigger_schema);
  
  	appendStringInfoChar(&buf, '\n');
  
***************
*** 2377,2382 **** send_message_to_server_log(ErrorData *edata)
--- 2620,2702 ----
  				appendStringInfo(&buf, _("LOCATION:  %s:%d\n"),
  								 edata->filename, edata->lineno);
  			}
+ 			if (edata->column_name)
+ 			{
+ 				log_line_prefix(&buf, edata);
+ 				appendStringInfoString(&buf, _("COLUMN NAME:  "));
+ 				append_with_tabs(&buf, edata->column_name);
+ 				appendStringInfoChar(&buf, '\n');
+ 			}
+ 			if (edata->table_name)
+ 			{
+ 				log_line_prefix(&buf, edata);
+ 				appendStringInfoString(&buf, _("TABLE NAME:  "));
+ 				append_with_tabs(&buf, edata->table_name);
+ 				appendStringInfoChar(&buf, '\n');
+ 			}
+ 			if (edata->schema_name)
+ 			{
+ 				log_line_prefix(&buf, edata);
+ 				appendStringInfoString(&buf, _("SCHEMA NAME:  "));
+ 				append_with_tabs(&buf, edata->schema_name);
+ 				appendStringInfoChar(&buf, '\n');
+ 			}
+ 			if (edata->constraint_name)
+ 			{
+ 				log_line_prefix(&buf, edata);
+ 				appendStringInfoString(&buf, _("CONSTRAINT NAME:  "));
+ 				append_with_tabs(&buf, edata->constraint_name);
+ 				appendStringInfoChar(&buf, '\n');
+ 			}
+ 			if (edata->constraint_table)
+ 			{
+ 				log_line_prefix(&buf, edata);
+ 				appendStringInfoString(&buf, _("CONSTRAINT TABLE:  "));
+ 				append_with_tabs(&buf, edata->constraint_table);
+ 				appendStringInfoChar(&buf, '\n');
+ 			}
+ 			if (edata->constraint_schema)
+ 			{
+ 				log_line_prefix(&buf, edata);
+ 				appendStringInfoString(&buf, _("CONSTRAINT SCHEMA:  "));
+ 				append_with_tabs(&buf, edata->constraint_schema);
+ 				appendStringInfoChar(&buf, '\n');
+ 			}
+ 			if (edata->routine_name)
+ 			{
+ 				log_line_prefix(&buf, edata);
+ 				appendStringInfoString(&buf, _("ROUTINE NAME:  "));
+ 				append_with_tabs(&buf, edata->routine_name);
+ 				appendStringInfoChar(&buf, '\n');
+ 			}
+ 			if (edata->routine_schema)
+ 			{
+ 				log_line_prefix(&buf, edata);
+ 				appendStringInfoString(&buf, _("ROUTINE SCHEMA:  "));
+ 				append_with_tabs(&buf, edata->routine_schema);
+ 				appendStringInfoChar(&buf, '\n');
+ 			}
+ 			if (edata->trigger_name)
+ 			{
+ 				log_line_prefix(&buf, edata);
+ 				appendStringInfoString(&buf, _("TRIGGER NAME:  "));
+ 				append_with_tabs(&buf, edata->trigger_name);
+ 				appendStringInfoChar(&buf, '\n');
+ 			}
+ 			if (edata->trigger_table)
+ 			{
+ 				log_line_prefix(&buf, edata);
+ 				appendStringInfoString(&buf, _("TRIGGER TABLE:  "));
+ 				append_with_tabs(&buf, edata->trigger_table);
+ 				appendStringInfoChar(&buf, '\n');
+ 			}
+ 			if (edata->trigger_schema)
+ 			{
+ 				log_line_prefix(&buf, edata);
+ 				appendStringInfoString(&buf, _("TRIGGER SCHEMA:  "));
+ 				append_with_tabs(&buf, edata->trigger_schema);
+ 				appendStringInfoChar(&buf, '\n');
+ 			}
  		}
  	}
  
***************
*** 2673,2678 **** send_message_to_frontend(ErrorData *edata)
--- 2993,3064 ----
  			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->schema_name)
+ 		{
+ 			pq_sendbyte(&msgbuf, PG_DIAG_SCHEMA_NAME);
+ 			err_sendstring(&msgbuf, edata->schema_name);
+ 		}
+ 
+ 		if (edata->constraint_name)
+ 		{
+ 			pq_sendbyte(&msgbuf, PG_DIAG_CONSTRAINT_NAME);
+ 			err_sendstring(&msgbuf, edata->constraint_name);
+ 		}
+ 
+ 		if (edata->constraint_table)
+ 		{
+ 			pq_sendbyte(&msgbuf, PG_DIAG_CONSTRAINT_TABLE);
+ 			err_sendstring(&msgbuf, edata->constraint_table);
+ 		}
+ 
+ 		if (edata->constraint_schema)
+ 		{
+ 			pq_sendbyte(&msgbuf, PG_DIAG_CONSTRAINT_SCHEMA);
+ 			err_sendstring(&msgbuf, edata->constraint_schema);
+ 		}
+ 
+ 		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->trigger_name)
+ 		{
+ 			pq_sendbyte(&msgbuf, PG_DIAG_TRIGGER_NAME);
+ 			err_sendstring(&msgbuf, edata->trigger_name);
+ 		}
+ 
+ 		if (edata->trigger_table)
+ 		{
+ 			pq_sendbyte(&msgbuf, PG_DIAG_TRIGGER_TABLE);
+ 			err_sendstring(&msgbuf, edata->trigger_table);
+ 		}
+ 
+ 		if (edata->trigger_schema)
+ 		{
+ 			pq_sendbyte(&msgbuf, PG_DIAG_TRIGGER_SCHEMA);
+ 			err_sendstring(&msgbuf, edata->trigger_schema);
+ 		}
+ 
  		pq_sendbyte(&msgbuf, '\0');		/* terminator */
  	}
  	else
*** /dev/null
--- b/src/backend/utils/error/relerror.c
***************
*** 0 ****
--- 1,63 ----
+ /*-------------------------------------------------------------------------
+  *
+  * relerror.c
+  *	  relation error loging functions
+  *
+  *
+  * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *
+  * IDENTIFICATION
+  *	  src/backend/utils/error/relerror.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "utils/elog.h"
+ #include "utils/lsyscache.h"
+ #include "utils/rel.h"
+ 
+ /*
+  * errrelcol --- sets column_name, table_name and schema_name of a column
+  * within errordata
+  */
+ int
+ errrelcol(Relation rel, const char *colname)
+ {
+ 	err_generic_string(PG_DIAG_COLUMN_NAME, colname);
+ 	err_generic_string(PG_DIAG_TABLE_NAME, RelationGetRelationName(rel));
+ 	err_generic_string(PG_DIAG_SCHEMA_NAME,
+ 			get_namespace_name(RelationGetNamespace(rel)));
+ 
+ 	return 0;
+ }
+ 
+ /*
+  * errrel --- sets column_name, table_name and schema_name within errordata
+  */
+ int
+ errrel(Relation rel)
+ {
+ 	err_generic_string(PG_DIAG_TABLE_NAME, RelationGetRelationName(rel));
+ 	err_generic_string(PG_DIAG_SCHEMA_NAME,
+ 			get_namespace_name(RelationGetNamespace(rel)));
+ 
+ 	return 0;
+ }
+ 
+ /*
+  * errcontraint --- sets constraint_name, constraint_table and constraint_schema
+  * within errordata.
+  */
+ int
+ errconstraint(Relation rel, const char *cname)
+ {
+ 	err_generic_string(PG_DIAG_CONSTRAINT_NAME, cname);
+ 	err_generic_string(PG_DIAG_CONSTRAINT_TABLE, RelationGetRelationName(rel));
+ 	err_generic_string(PG_DIAG_CONSTRAINT_SCHEMA,
+ 			get_namespace_name(RelationGetNamespace(rel)));
+ 
+ 	return 0;
+ }
*** a/src/backend/utils/sort/tuplesort.c
--- b/src/backend/utils/sort/tuplesort.c
***************
*** 3091,3097 **** comparetup_index_btree(const SortTuple *a, const SortTuple *b,
  						RelationGetRelationName(state->indexRel)),
  				 errdetail("Key %s is duplicated.",
  						   BuildIndexValueDescription(state->indexRel,
! 													  values, isnull))));
  	}
  
  	/*
--- 3091,3098 ----
  						RelationGetRelationName(state->indexRel)),
  				 errdetail("Key %s is duplicated.",
  						   BuildIndexValueDescription(state->indexRel,
! 													  values, isnull)),
! 				 errrel(state->indexRel)));
  	}
  
  	/*
*** a/src/include/postgres_ext.h
--- b/src/include/postgres_ext.h
***************
*** 60,64 **** typedef PG_INT64_TYPE pg_int64;
--- 60,75 ----
  #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_SCHEMA_NAME			's'
+ #define PG_DIAG_CONSTRAINT_NAME		'n'
+ #define PG_DIAG_CONSTRAINT_TABLE	'o'
+ #define PG_DIAG_CONSTRAINT_SCHEMA	'm'
+ #define PG_DIAG_ROUTINE_NAME		'r'
+ #define PG_DIAG_ROUTINE_SCHEMA		'u'
+ #define PG_DIAG_TRIGGER_NAME		'g'
+ #define PG_DIAG_TRIGGER_TABLE		'i'
+ #define PG_DIAG_TRIGGER_SCHEMA		'h'
  
  #endif   /* POSTGRES_EXT_H */
*** a/src/include/utils/elog.h
--- b/src/include/utils/elog.h
***************
*** 195,200 **** extern int	geterrcode(void);
--- 195,202 ----
  extern int	geterrposition(void);
  extern int	getinternalerrposition(void);
  
+ extern int	err_generic_string(int field, const char *str);
+ 
  
  /*----------
   * Old-style error reporting API: to be used in this way:
***************
*** 326,335 **** typedef struct ErrorData
  	char	   *detail_log;		/* detail error message for server log only */
  	char	   *hint;			/* hint message */
  	char	   *context;		/* context message */
! 	int			cursorpos;		/* cursor index into query string */
! 	int			internalpos;	/* cursor index into internalquery */
! 	char	   *internalquery;	/* text of internally-generated query */
! 	int			saved_errno;	/* errno at entry */
  } ErrorData;
  
  extern void EmitErrorReport(void);
--- 328,348 ----
  	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	   *schema_name;	/* name of schema */
! 	char	   *constraint_name;		/* name of constraint */
! 	char	   *constraint_table;		/* name of table related to constraint */
! 	char	   *constraint_schema;		/* name of schema with constraint */
! 	char	   *routine_name;			/* name of function that caused error */
! 	char	   *routine_schema;			/* schema name of function that caused error */
! 	char	   *trigger_name;			/* name of trigger that caused error */
! 	char	   *trigger_table;			/* table of trigger that caused error */
! 	char	   *trigger_schema;			/* schema of trigger that caused error */
! 	int			cursorpos;				/* cursor index into query string */
! 	int			internalpos;			/* cursor index into internalquery */
! 	char	   *internalquery;			/* text of internally-generated query */
! 	int			saved_errno;			/* errno at entry */
  } ErrorData;
  
  extern void EmitErrorReport(void);
*** a/src/include/utils/rel.h
--- b/src/include/utils/rel.h
***************
*** 394,397 **** typedef struct StdRdOptions
--- 394,402 ----
  extern void RelationIncrementReferenceCount(Relation rel);
  extern void RelationDecrementReferenceCount(Relation rel);
  
+ /* routines in utils/error/relerror.c */
+ extern int errrelcol(Relation rel, const char *colname);
+ extern int errrel(Relation rel);
+ extern int errconstraint(Relation rel, 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,986 ----
  								  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_SCHEMA_NAME);
+ 		if (val)
+ 			appendPQExpBuffer(&workBuf,
+ 								libpq_gettext("SCHEMA 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_CONSTRAINT_TABLE);
+ 		if (val)
+ 			appendPQExpBuffer(&workBuf,
+ 								libpq_gettext("CONSTRAINT TABLE:  %s\n"), val);
+ 		val = PQresultErrorField(res, PG_DIAG_CONSTRAINT_SCHEMA);
+ 		if (val)
+ 			appendPQExpBuffer(&workBuf,
+ 								libpq_gettext("CONSTRAINT SCHEMA:  %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 SCHEMA:  %s\n"), val);
+ 		val = PQresultErrorField(res, PG_DIAG_TRIGGER_NAME);
+ 		if (val)
+ 			appendPQExpBuffer(&workBuf,
+ 								libpq_gettext("TRIGGER NAME:  %s\n"), val);
+ 		val = PQresultErrorField(res, PG_DIAG_TRIGGER_TABLE);
+ 		if (val)
+ 			appendPQExpBuffer(&workBuf,
+ 								libpq_gettext("TRIGGER TABLE:  %s\n"), val);
+ 		val = PQresultErrorField(res, PG_DIAG_TRIGGER_SCHEMA);
+ 		if (val)
+ 			appendPQExpBuffer(&workBuf,
+ 								libpq_gettext("TRIGGER SCHEMA:  %s\n"), val);
  	}
  
  	/*
