diff -cpr head/src/backend/access/common/indextuple.c report_dupkey/src/backend/access/common/indextuple.c *** head/src/backend/access/common/indextuple.c 2009-07-21 13:25:59.100113842 +0900 --- report_dupkey/src/backend/access/common/indextuple.c 2009-07-21 13:26:10.311181100 +0900 *************** *** 19,24 **** --- 19,26 ---- #include "access/heapam.h" #include "access/itup.h" #include "access/tuptoaster.h" + #include "utils/lsyscache.h" + #include "utils/rel.h" /* ---------------------------------------------------------------- *************** CopyIndexTuple(IndexTuple source) *** 446,448 **** --- 448,502 ---- memcpy(result, source, size); return result; } + + /* + * Report duplicated key error. + */ + void + report_unique_violation(Relation rel, IndexTuple itup) + { + StringInfoData key_names; + StringInfoData key_values; + int i; + TupleDesc tupdesc = rel->rd_att; + + /* Get printable versions of the keys involved */ + initStringInfo(&key_names); + initStringInfo(&key_values); + for (i = 0; i < tupdesc->natts; i++) + { + char *name; + char *val; + Datum value; + bool isnull; + + 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); + } + + if (i > 0) + { + appendStringInfoChar(&key_names, ','); + appendStringInfoChar(&key_values, ','); + } + appendStringInfoString(&key_names, name); + appendStringInfoString(&key_values, 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.data, key_values.data))); + } diff -cpr head/src/backend/access/nbtree/nbtinsert.c report_dupkey/src/backend/access/nbtree/nbtinsert.c *** head/src/backend/access/nbtree/nbtinsert.c 2009-07-21 13:25:59.099109481 +0900 --- report_dupkey/src/backend/access/nbtree/nbtinsert.c 2009-07-21 13:26:10.312077984 +0900 *************** _bt_check_unique(Relation rel, IndexTupl *** 295,304 **** break; } ! ereport(ERROR, ! (errcode(ERRCODE_UNIQUE_VIOLATION), ! errmsg("duplicate key value violates unique constraint \"%s\"", ! RelationGetRelationName(rel)))); } else if (all_dead) { --- 295,302 ---- break; } ! /* duplicate key error */ ! report_unique_violation(rel, itup); } else if (all_dead) { diff -cpr head/src/backend/utils/adt/ri_triggers.c report_dupkey/src/backend/utils/adt/ri_triggers.c *** head/src/backend/utils/adt/ri_triggers.c 2009-07-21 13:25:59.067110675 +0900 --- report_dupkey/src/backend/utils/adt/ri_triggers.c 2009-07-21 13:26:10.313181065 +0900 *************** ri_ReportViolation(RI_QueryKey *qkey, co *** 3413,3426 **** HeapTuple violator, TupleDesc tupdesc, bool spi_err) { ! #define BUFLENGTH 512 ! char key_names[BUFLENGTH]; ! char key_values[BUFLENGTH]; ! char *name_ptr = key_names; ! char *val_ptr = key_values; ! bool onfk; ! int idx, ! key_idx; if (spi_err) ereport(ERROR, --- 3413,3423 ---- HeapTuple violator, TupleDesc tupdesc, bool spi_err) { ! StringInfoData key_names; ! StringInfoData key_values; ! bool onfk; ! int idx, ! key_idx; if (spi_err) ereport(ERROR, *************** ri_ReportViolation(RI_QueryKey *qkey, co *** 3465,3495 **** } /* Get printable versions of the keys involved */ for (idx = 0; idx < qkey->nkeypairs; idx++) { int fnum = qkey->keypair[idx][key_idx]; ! char *name, ! *val; name = SPI_fname(tupdesc, fnum); val = SPI_getvalue(violator, tupdesc, fnum); if (!val) val = "null"; ! /* ! * 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", idx > 0 ? "," : "", name); ! val_ptr += sprintf(val_ptr, "%s%s", idx > 0 ? "," : "", val); } if (onfk) --- 3462,3487 ---- } /* Get printable versions of the keys involved */ + initStringInfo(&key_names); + initStringInfo(&key_values); for (idx = 0; idx < qkey->nkeypairs; idx++) { int fnum = qkey->keypair[idx][key_idx]; ! char *name; ! char *val; name = SPI_fname(tupdesc, fnum); val = SPI_getvalue(violator, tupdesc, fnum); if (!val) val = "null"; ! if (idx > 0) { ! appendStringInfoChar(&key_names, ','); ! appendStringInfoChar(&key_values, ','); } ! appendStringInfoString(&key_names, name); ! appendStringInfoString(&key_values, val); } if (onfk) *************** ri_ReportViolation(RI_QueryKey *qkey, co *** 3498,3504 **** errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"", RelationGetRelationName(fk_rel), constrname), errdetail("Key (%s)=(%s) is not present in table \"%s\".", ! key_names, key_values, RelationGetRelationName(pk_rel)))); else ereport(ERROR, --- 3490,3496 ---- errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"", RelationGetRelationName(fk_rel), constrname), errdetail("Key (%s)=(%s) is not present in table \"%s\".", ! key_names.data, key_values.data, RelationGetRelationName(pk_rel)))); else ereport(ERROR, *************** ri_ReportViolation(RI_QueryKey *qkey, co *** 3507,3513 **** RelationGetRelationName(pk_rel), constrname, RelationGetRelationName(fk_rel)), errdetail("Key (%s)=(%s) is still referenced from table \"%s\".", ! key_names, key_values, RelationGetRelationName(fk_rel)))); } --- 3499,3505 ---- RelationGetRelationName(pk_rel), constrname, RelationGetRelationName(fk_rel)), errdetail("Key (%s)=(%s) is still referenced from table \"%s\".", ! key_names.data, key_values.data, RelationGetRelationName(fk_rel)))); } diff -cpr head/src/include/access/itup.h report_dupkey/src/include/access/itup.h *** head/src/include/access/itup.h 2009-07-21 13:25:59.126109412 +0900 --- report_dupkey/src/include/access/itup.h 2009-07-21 13:26:10.313181065 +0900 *************** *** 18,23 **** --- 18,24 ---- #include "access/tupmacs.h" #include "storage/bufpage.h" #include "storage/itemptr.h" + #include "utils/relcache.h" /* * Index tuple header structure *************** extern IndexTuple index_form_tuple(Tuple *** 144,148 **** --- 145,150 ---- extern Datum nocache_index_getattr(IndexTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull); extern IndexTuple CopyIndexTuple(IndexTuple source); + extern void report_unique_violation(Relation rel, IndexTuple itup); #endif /* ITUP_H */ diff -cpr head/src/test/regress/expected/alter_table.out report_dupkey/src/test/regress/expected/alter_table.out *** head/src/test/regress/expected/alter_table.out 2009-07-21 13:25:59.166236124 +0900 --- report_dupkey/src/test/regress/expected/alter_table.out 2009-07-21 13:28:53.321058000 +0900 *************** insert into atacc1 (test) values (2); *** 407,412 **** --- 407,413 ---- -- should fail insert into atacc1 (test) values (2); ERROR: duplicate key value violates unique constraint "atacc_test1" + DETAIL: Key (test)=(2) already exists. -- should succeed insert into atacc1 (test) values (4); -- try adding a unique oid constraint *************** insert into atacc1 (test,test2) values ( *** 442,447 **** --- 443,449 ---- -- should fail insert into atacc1 (test,test2) values (4,4); ERROR: duplicate key value violates unique constraint "atacc_test1" + DETAIL: Key (test,test2)=(4,4) already exists. -- should all succeed insert into atacc1 (test,test2) values (4,5); insert into atacc1 (test,test2) values (5,4); *************** NOTICE: ALTER TABLE / ADD UNIQUE will c *** 456,461 **** --- 458,464 ---- insert into atacc1 (test2, test) values (3, 3); insert into atacc1 (test2, test) values (2, 3); ERROR: duplicate key value violates unique constraint "atacc1_test_key" + DETAIL: Key (test)=(3) already exists. drop table atacc1; -- test primary key constraint adding create table atacc1 ( test int ) with oids; *************** insert into atacc1 (test) values (2); *** 467,472 **** --- 470,476 ---- -- should fail insert into atacc1 (test) values (2); ERROR: duplicate key value violates unique constraint "atacc_test1" + DETAIL: Key (test)=(2) already exists. -- should succeed insert into atacc1 (test) values (4); -- inserting NULL should fail *************** insert into atacc1 (test,test2) values ( *** 534,539 **** --- 538,544 ---- -- should fail insert into atacc1 (test,test2) values (4,4); ERROR: duplicate key value violates unique constraint "atacc_test1" + DETAIL: Key (test,test2)=(4,4) already exists. insert into atacc1 (test,test2) values (NULL,3); ERROR: null value in column "test" violates not-null constraint insert into atacc1 (test,test2) values (3, NULL); *************** NOTICE: CREATE TABLE / PRIMARY KEY will *** 552,557 **** --- 557,563 ---- insert into atacc1 (test2, test) values (3, 3); insert into atacc1 (test2, test) values (2, 3); ERROR: duplicate key value violates unique constraint "atacc1_pkey" + DETAIL: Key (test)=(3) already exists. insert into atacc1 (test2, test) values (1, NULL); ERROR: null value in column "test" violates not-null constraint drop table atacc1; diff -cpr head/src/test/regress/expected/arrays.out report_dupkey/src/test/regress/expected/arrays.out *** head/src/test/regress/expected/arrays.out 2009-07-21 13:25:59.163208473 +0900 --- report_dupkey/src/test/regress/expected/arrays.out 2009-07-21 13:28:48.187241000 +0900 *************** insert into arr_tbl values ('{1,2}'); *** 713,718 **** --- 713,719 ---- -- failure expected: insert into arr_tbl values ('{1,2,3}'); ERROR: duplicate key value violates unique constraint "arr_tbl_f1_key" + DETAIL: Key (f1)=({1,2,3}) already exists. insert into arr_tbl values ('{2,3,4}'); insert into arr_tbl values ('{1,5,3}'); insert into arr_tbl values ('{1,2,10}'); diff -cpr head/src/test/regress/expected/create_index.out report_dupkey/src/test/regress/expected/create_index.out *** head/src/test/regress/expected/create_index.out 2009-07-21 13:25:59.162211307 +0900 --- report_dupkey/src/test/regress/expected/create_index.out 2009-07-21 13:28:47.262113000 +0900 *************** INSERT INTO func_index_heap VALUES('QWE' *** 562,567 **** --- 562,568 ---- -- this should fail because of unique index: INSERT INTO func_index_heap VALUES('ABCD', 'EF'); ERROR: duplicate key value violates unique constraint "func_index_index" + DETAIL: Key (pg_expression_1)=(ABCDEF) already exists. -- but this shouldn't: INSERT INTO func_index_heap VALUES('QWERTY'); -- *************** INSERT INTO func_index_heap VALUES('QWE' *** 576,581 **** --- 577,583 ---- -- this should fail because of unique index: INSERT INTO func_index_heap VALUES('ABCD', 'EF'); ERROR: duplicate key value violates unique constraint "func_index_index" + DETAIL: Key (pg_expression_1)=(ABCDEF) already exists. -- but this shouldn't: INSERT INTO func_index_heap VALUES('QWERTY'); -- *************** CREATE UNIQUE INDEX CONCURRENTLY concur_ *** 600,605 **** --- 602,608 ---- -- check if constraint is set up properly to be enforced INSERT INTO concur_heap VALUES ('b','x'); ERROR: duplicate key value violates unique constraint "concur_index2" + 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" diff -cpr head/src/test/regress/expected/inherit.out report_dupkey/src/test/regress/expected/inherit.out *** head/src/test/regress/expected/inherit.out 2009-07-21 13:25:59.161085870 +0900 --- report_dupkey/src/test/regress/expected/inherit.out 2009-07-21 13:28:46.989122000 +0900 *************** NOTICE: CREATE TABLE / PRIMARY KEY will *** 638,643 **** --- 638,644 ---- INSERT INTO inhg VALUES (5, 10); INSERT INTO inhg VALUES (20, 10); -- should fail ERROR: duplicate key value violates unique constraint "inhg_pkey" + DETAIL: Key (xx)=(10) already exists. DROP TABLE inhg; /* Multiple primary keys creation should fail */ CREATE TABLE inhg (x text, LIKE inhx INCLUDING INDEXES, PRIMARY KEY(x)); /* fails */ *************** INSERT INTO inhg (xx, yy, x) VALUES ('te *** 653,658 **** --- 654,660 ---- INSERT INTO inhg (xx, yy, x) VALUES ('test', 10, 15); INSERT INTO inhg (xx, yy, x) VALUES ('foo', 10, 15); -- should fail ERROR: duplicate key value violates unique constraint "inhg_x_key" + DETAIL: Key (x)=(15) already exists. DROP TABLE inhg; DROP TABLE inhz; -- Test changing the type of inherited columns diff -cpr head/src/test/regress/expected/plpgsql.out report_dupkey/src/test/regress/expected/plpgsql.out *** head/src/test/regress/expected/plpgsql.out 2009-07-21 13:25:59.164211909 +0900 --- report_dupkey/src/test/regress/expected/plpgsql.out 2009-07-21 13:28:55.247245000 +0900 *************** select * from PField_v1 where pfname = ' *** 1517,1522 **** --- 1517,1523 ---- -- insert into PField values ('PF1_1', 'should fail due to unique index'); ERROR: duplicate key value violates unique constraint "pfield_name" + DETAIL: Key (name)=(PF1_1) already exists. update PSlot set backlink = 'WS.not.there' where slotname = 'PS.base.a1'; ERROR: WS.not.there does not exist CONTEXT: PL/pgSQL function "tg_backlink_a" line 16 at assignment *************** ERROR: illegal slotlink beginning with *** 1531,1536 **** --- 1532,1538 ---- CONTEXT: PL/pgSQL function "tg_slotlink_a" line 16 at assignment insert into HSlot values ('HS', 'base.hub1', 1, ''); ERROR: duplicate key value violates unique constraint "hslot_name" + DETAIL: Key (slotname)=(HS.base.hub1.1 ) already exists. insert into HSlot values ('HS', 'base.hub1', 20, ''); ERROR: no manual manipulation of HSlot delete from HSlot; diff -cpr head/src/test/regress/expected/transactions.out report_dupkey/src/test/regress/expected/transactions.out *** head/src/test/regress/expected/transactions.out 2009-07-21 13:25:59.167113594 +0900 --- report_dupkey/src/test/regress/expected/transactions.out 2009-07-21 13:28:48.185503000 +0900 *************** NOTICE: CREATE TABLE / UNIQUE will crea *** 468,479 **** --- 468,481 ---- INSERT INTO koju VALUES (1); INSERT INTO koju VALUES (1); ERROR: duplicate key value violates unique constraint "koju_a_key" + DETAIL: Key (a)=(1) already exists. rollback to x; CREATE TABLE koju (a INT UNIQUE); NOTICE: CREATE TABLE / UNIQUE will create implicit index "koju_a_key" for table "koju" INSERT INTO koju VALUES (1); INSERT INTO koju VALUES (1); ERROR: duplicate key value violates unique constraint "koju_a_key" + DETAIL: Key (a)=(1) already exists. ROLLBACK; DROP TABLE foo; DROP TABLE baz; diff -cpr head/src/test/regress/expected/uuid.out report_dupkey/src/test/regress/expected/uuid.out *** head/src/test/regress/expected/uuid.out 2009-07-21 13:25:59.164211909 +0900 --- report_dupkey/src/test/regress/expected/uuid.out 2009-07-21 13:28:45.614492000 +0900 *************** CREATE UNIQUE INDEX guid1_unique_BTREE O *** 119,124 **** --- 119,125 ---- -- should fail INSERT INTO guid1(guid_field) VALUES('11111111-1111-1111-1111-111111111111'); ERROR: duplicate key value violates unique constraint "guid1_unique_btree" + DETAIL: Key (guid_field)=(11111111-1111-1111-1111-111111111111) already exists. -- check to see whether the new indexes are actually there SELECT count(*) FROM pg_class WHERE relkind='i' AND relname LIKE 'guid%'; count diff -cpr head/src/test/regress/output/constraints.source report_dupkey/src/test/regress/output/constraints.source *** head/src/test/regress/output/constraints.source 2009-07-21 13:25:59.159206402 +0900 --- report_dupkey/src/test/regress/output/constraints.source 2009-07-21 13:46:00.794458000 +0900 *************** INSERT INTO PRIMARY_TBL VALUES (1, 'one' *** 294,299 **** --- 294,300 ---- INSERT INTO PRIMARY_TBL VALUES (2, 'two'); INSERT INTO PRIMARY_TBL VALUES (1, 'three'); ERROR: duplicate key value violates unique constraint "primary_tbl_pkey" + DETAIL: Key (i)=(1) already exists. INSERT INTO PRIMARY_TBL VALUES (4, 'three'); INSERT INTO PRIMARY_TBL VALUES (5, 'one'); INSERT INTO PRIMARY_TBL (t) VALUES ('six'); *************** INSERT INTO UNIQUE_TBL VALUES (1, 'one') *** 338,343 **** --- 339,345 ---- INSERT INTO UNIQUE_TBL VALUES (2, 'two'); INSERT INTO UNIQUE_TBL VALUES (1, 'three'); ERROR: duplicate key value violates unique constraint "unique_tbl_i_key" + DETAIL: Key (i)=(1) already exists. INSERT INTO UNIQUE_TBL VALUES (4, 'four'); INSERT INTO UNIQUE_TBL VALUES (5, 'one'); INSERT INTO UNIQUE_TBL (t) VALUES ('six'); *************** INSERT INTO UNIQUE_TBL VALUES (2, 'two') *** 362,367 **** --- 364,370 ---- INSERT INTO UNIQUE_TBL VALUES (1, 'three'); INSERT INTO UNIQUE_TBL VALUES (1, 'one'); ERROR: duplicate key value violates unique constraint "unique_tbl_i_key" + DETAIL: Key (i,t)=(1,one) already exists. INSERT INTO UNIQUE_TBL VALUES (5, 'one'); INSERT INTO UNIQUE_TBL (t) VALUES ('six'); SELECT '' AS five, * FROM UNIQUE_TBL; diff -cpr head/src/test/regress/output/tablespace.source report_dupkey/src/test/regress/output/tablespace.source *** head/src/test/regress/output/tablespace.source 2009-07-21 13:25:59.159206402 +0900 --- report_dupkey/src/test/regress/output/tablespace.source 2009-07-21 13:45:22.251108818 +0900 *************** ALTER INDEX testschema.anindex SET TABLE *** 49,54 **** --- 49,55 ---- INSERT INTO testschema.atable VALUES(3); -- ok INSERT INTO testschema.atable VALUES(1); -- fail (checks index) ERROR: duplicate key value violates unique constraint "anindex" + DETAIL: Key (column1)=(1) already exists. SELECT COUNT(*) FROM testschema.atable; -- checks heap count -------