From 527f0ae3fa48c3c3a8ba1bde19039545e88a52b6 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat, 1 Aug 2009 20:59:17 +0000
Subject: [PATCH] Department of second thoughts: let's show the exact key
 during unique index build failures, too.  Refactor a bit more since that
 error message isn't spelled the same.

---
 src/backend/access/index/genam.c           | 38 +++++++++++-----------
 src/backend/access/nbtree/nbtinsert.c      | 12 +++++--
 src/backend/utils/sort/tuplesort.c         | 12 +++++--
 src/include/access/genam.h                 |  6 ++--
 src/test/regress/expected/alter_table.out  |  4 +--
 src/test/regress/expected/create_index.out |  2 +-
 6 files changed, 44 insertions(+), 30 deletions(-)

diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index 6482aaa6a70..860e5766034 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/index/genam.c,v 1.75 2009/08/01 19:59:41 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/index/genam.c,v 1.76 2009/08/01 20:59:17 tgl Exp $
  *
  * NOTES
  *	  many of the old access method routines have been turned into
@@ -133,13 +133,16 @@ IndexScanEnd(IndexScanDesc scan)
 }
 
 /*
- * ReportUniqueViolation -- Report a unique-constraint violation.
+ * BuildIndexValueDescription
  *
- * The index entry represented by values[]/isnull[] violates the unique
- * constraint enforced by this index.  Throw a suitable error.
+ * Construct a string describing the contents of an index entry, in the
+ * form "(key_name, ...)=(key_value, ...)".  This is currently used
+ * only for building unique-constraint error messages, but we don't want
+ * to hardwire the spelling of the messages here.
  */
-void
-ReportUniqueViolation(Relation indexRelation, Datum *values, bool *isnull)
+char *
+BuildIndexValueDescription(Relation indexRelation,
+						   Datum *values, bool *isnull)
 {
 	/*
 	 * XXX for the moment we use the index's tupdesc as a guide to the
@@ -148,14 +151,14 @@ ReportUniqueViolation(Relation indexRelation, Datum *values, bool *isnull)
 	 * are ever to support non-btree unique indexes.
 	 */
 	TupleDesc	tupdesc = RelationGetDescr(indexRelation);
-	char	   *key_names;
-	StringInfoData key_values;
+	StringInfoData buf;
 	int			i;
 
-	key_names = pg_get_indexdef_columns(RelationGetRelid(indexRelation), true);
+	initStringInfo(&buf);
+	appendStringInfo(&buf, "(%s)=(",
+					 pg_get_indexdef_columns(RelationGetRelid(indexRelation),
+											 true));
 
-	/* Get printable versions of the key values */
-	initStringInfo(&key_values);
 	for (i = 0; i < tupdesc->natts; i++)
 	{
 		char   *val;
@@ -173,16 +176,13 @@ ReportUniqueViolation(Relation indexRelation, Datum *values, bool *isnull)
 		}
 
 		if (i > 0)
-			appendStringInfoString(&key_values, ", ");
-		appendStringInfoString(&key_values, val);
+			appendStringInfoString(&buf, ", ");
+		appendStringInfoString(&buf, val);
 	}
 
-	ereport(ERROR,
-			(errcode(ERRCODE_UNIQUE_VIOLATION),
-			 errmsg("duplicate key value violates unique constraint \"%s\"",
-					RelationGetRelationName(indexRelation)),
-			 errdetail("Key (%s)=(%s) already exists.",
-					   key_names, key_values.data)));
+	appendStringInfoChar(&buf, ')');
+
+	return buf.data;
 }
 
 
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index ee3d89fd066..dd3736ad8a9 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.172 2009/08/01 19:59:41 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.173 2009/08/01 20:59:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -365,7 +365,7 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
 					 * This is a definite conflict.  Break the tuple down
 					 * into datums and report the error.  But first, make
 					 * sure we release the buffer locks we're holding ---
-					 * the error reporting code could make catalog accesses,
+					 * BuildIndexValueDescription could make catalog accesses,
 					 * which in the worst case might touch this same index
 					 * and cause deadlocks.
 					 */
@@ -379,7 +379,13 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
 
 						index_deform_tuple(itup, RelationGetDescr(rel),
 										   values, isnull);
-						ReportUniqueViolation(rel, values, isnull);
+						ereport(ERROR,
+								(errcode(ERRCODE_UNIQUE_VIOLATION),
+								 errmsg("duplicate key value violates unique constraint \"%s\"",
+										RelationGetRelationName(rel)),
+								 errdetail("Key %s already exists.",
+										   BuildIndexValueDescription(rel,
+															values, isnull))));
 					}
 				}
 				else if (all_dead)
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index 6d07296d596..cb89f65a914 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -91,7 +91,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/sort/tuplesort.c,v 1.91 2009/06/11 14:49:06 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/sort/tuplesort.c,v 1.92 2009/08/01 20:59:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2801,11 +2801,19 @@ comparetup_index_btree(const SortTuple *a, const SortTuple *b,
 	 * error in that case.
 	 */
 	if (state->enforceUnique && !equal_hasnull && tuple1 != tuple2)
+	{
+		Datum	values[INDEX_MAX_KEYS];
+		bool	isnull[INDEX_MAX_KEYS];
+
+		index_deform_tuple(tuple1, tupDes, values, isnull);
 		ereport(ERROR,
 				(errcode(ERRCODE_UNIQUE_VIOLATION),
 				 errmsg("could not create unique index \"%s\"",
 						RelationGetRelationName(state->indexRel)),
-				 errdetail("Table contains duplicated values.")));
+				 errdetail("Key %s is duplicated.",
+						   BuildIndexValueDescription(state->indexRel,
+													  values, isnull))));
+	}
 
 	/*
 	 * If key values are equal, we sort on ItemPointer.  This does not affect
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index 19025265775..068d96493db 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/genam.h,v 1.80 2009/08/01 19:59:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/access/genam.h,v 1.81 2009/08/01 20:59:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -164,8 +164,8 @@ extern FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum,
 extern IndexScanDesc RelationGetIndexScan(Relation indexRelation,
 					 int nkeys, ScanKey key);
 extern void IndexScanEnd(IndexScanDesc scan);
-extern void ReportUniqueViolation(Relation indexRelation,
-								  Datum *values, bool *isnull);
+extern char *BuildIndexValueDescription(Relation indexRelation,
+						   Datum *values, bool *isnull);
 
 /*
  * heap-or-index access to system catalogs (in genam.c)
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 3c9bb6308ea..20bf3de3bad 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -423,7 +423,7 @@ insert into atacc1 (test) values (2);
 alter table atacc1 add constraint atacc_test1 unique (test);
 NOTICE:  ALTER TABLE / ADD UNIQUE will create implicit index "atacc_test1" for table "atacc1"
 ERROR:  could not create unique index "atacc_test1"
-DETAIL:  Table contains duplicated values.
+DETAIL:  Key (test)=(2) is duplicated.
 insert into atacc1 (test) values (3);
 drop table atacc1;
 -- let's do one where the unique constraint fails
@@ -494,7 +494,7 @@ insert into atacc1 (test) values (2);
 alter table atacc1 add constraint atacc_test1 primary key (test);
 NOTICE:  ALTER TABLE / ADD PRIMARY KEY will create implicit index "atacc_test1" for table "atacc1"
 ERROR:  could not create unique index "atacc_test1"
-DETAIL:  Table contains duplicated values.
+DETAIL:  Key (test)=(2) is duplicated.
 insert into atacc1 (test) values (3);
 drop table atacc1;
 -- let's do another one where the primary key constraint fails when added
diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out
index 02f2474a052..079a64098c5 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -685,7 +685,7 @@ DETAIL:  Key (f1)=(b) already exists.
 -- check if constraint is enforced properly at build time
 CREATE UNIQUE INDEX CONCURRENTLY concur_index3 ON concur_heap(f2);
 ERROR:  could not create unique index "concur_index3"
-DETAIL:  Table contains duplicated values.
+DETAIL:  Key (f2)=(b) is duplicated.
 -- test that expression indexes and partial indexes work concurrently
 CREATE INDEX CONCURRENTLY concur_index4 on concur_heap(f2) WHERE f1='a';
 CREATE INDEX CONCURRENTLY concur_index5 on concur_heap(f2) WHERE f1='x';
-- 
GitLab