Index: src/backend/access/nbtree/nbtinsert.c
===================================================================
--- src/backend/access/nbtree/nbtinsert.c	(head)
+++ src/backend/access/nbtree/nbtinsert.c	(working copy)
@@ -22,6 +22,7 @@
 #include "storage/bufmgr.h"
 #include "storage/lmgr.h"
 #include "utils/inval.h"
+#include "utils/lsyscache.h"
 #include "utils/tqual.h"
 
 
@@ -78,6 +79,7 @@
 static bool _bt_isequal(TupleDesc itupdesc, Page page, OffsetNumber offnum,
 			int keysz, ScanKey scankey);
 static void _bt_vacuum_one_page(Relation rel, Buffer buffer);
+static void _bt_unique_violation(Relation rel, IndexTuple itup);
 
 
 /*
@@ -295,10 +297,8 @@
 						break;
 					}
 
-					ereport(ERROR,
-							(errcode(ERRCODE_UNIQUE_VIOLATION),
-							 errmsg("duplicate key value violates unique constraint \"%s\"",
-									RelationGetRelationName(rel))));
+					/* duplicate key error */
+					_bt_unique_violation(rel, itup);
 				}
 				else if (all_dead)
 				{
@@ -355,7 +355,66 @@
 	return InvalidTransactionId;
 }
 
+/*
+ * borrowing from ri_ReportViolation.
+ */
+static void
+_bt_unique_violation(Relation rel, IndexTuple itup)
+{
+#define BUFLENGTH	512
+	char		key_names[BUFLENGTH];
+	char		key_values[BUFLENGTH];
+	char	   *name_ptr = key_names;
+	char	   *val_ptr = key_values;
+	int			i;
 
+	TupleDesc	tupdesc = rel->rd_att;
+
+	for (i = 0; i < tupdesc->natts; i++)
+	{
+		Datum		value;
+		bool		isnull;
+		char	   *name,
+				   *val;
+
+		name = NameStr(tupdesc->attrs[i]->attname);
+		value = index_getattr(itup, i + 1, tupdesc, &isnull);
+		if (isnull)
+			val = "null";
+		else
+		{
+			Oid			foutoid;
+			bool		typisvarlena;
+
+			getTypeOutputInfo(tupdesc->attrs[i]->atttypid,
+							  &foutoid, &typisvarlena);
+			val = OidOutputFunctionCall(foutoid, value);
+		}
+
+		/*
+		 * Go to "..." if name or value doesn't fit in buffer.  We reserve 5
+		 * bytes to ensure we can add comma, "...", null.
+		 */
+		if (strlen(name) >= (key_names + BUFLENGTH - 5) - name_ptr ||
+			strlen(val) >= (key_values + BUFLENGTH - 5) - val_ptr)
+		{
+			sprintf(name_ptr, "...");
+			sprintf(val_ptr, "...");
+			break;
+		}
+
+		name_ptr += sprintf(name_ptr, "%s%s", i > 0 ? "," : "", name);
+		val_ptr += sprintf(val_ptr, "%s%s", i > 0 ? "," : "", val);
+	}
+
+	ereport(ERROR,
+			(errcode(ERRCODE_UNIQUE_VIOLATION),
+			 errmsg("duplicate key value violates unique constraint \"%s\"",
+					RelationGetRelationName(rel)),
+		errdetail("Key (%s)=(%s) already exists.",
+				  key_names, key_values)));
+}
+
 /*
  *	_bt_findinsertloc() -- Finds an insert location for a tuple
  *
