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 *