values being complained of.
In passing, also remove the arbitrary length limitation in the similar
error detail message for foreign key violations.
Itagaki Takahiro
-- expected failures on duplicate key
INSERT INTO try (name) VALUES ('a');
ERROR: duplicate key value violates unique constraint "try_pkey"
+DETAIL: Key (name)=(a) already exists.
INSERT INTO try (name) VALUES ('A');
ERROR: duplicate key value violates unique constraint "try_pkey"
+DETAIL: Key (name)=(A) already exists.
INSERT INTO try (name) VALUES ('aB');
ERROR: duplicate key value violates unique constraint "try_pkey"
+DETAIL: Key (name)=(aB) already exists.
-- Make sure that citext_smaller() and citext_lager() work properly.
SELECT citext_smaller( 'aa'::citext, 'ab'::citext ) = 'aa' AS t;
t
-- expected failures on duplicate key
INSERT INTO try (name) VALUES ('a');
ERROR: duplicate key value violates unique constraint "try_pkey"
+DETAIL: Key (name)=(a) already exists.
INSERT INTO try (name) VALUES ('A');
ERROR: duplicate key value violates unique constraint "try_pkey"
+DETAIL: Key (name)=(A) already exists.
INSERT INTO try (name) VALUES ('aB');
ERROR: duplicate key value violates unique constraint "try_pkey"
+DETAIL: Key (name)=(aB) already exists.
-- Make sure that citext_smaller() and citext_lager() work properly.
SELECT citext_smaller( 'aa'::citext, 'ab'::citext ) = 'aa' AS t;
t
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/common/indextuple.c,v 1.88 2009/06/11 14:48:53 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/access/common/indextuple.c,v 1.89 2009/08/01 19:59:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
return fetchatt(att[attnum], tp + off);
}
+/*
+ * Convert an index tuple into Datum/isnull arrays.
+ *
+ * The caller must allocate sufficient storage for the output arrays.
+ * (INDEX_MAX_KEYS entries should be enough.)
+ */
+void
+index_deform_tuple(IndexTuple tup, TupleDesc tupleDescriptor,
+ Datum *values, bool *isnull)
+{
+ int i;
+
+ /* Assert to protect callers who allocate fixed-size arrays */
+ Assert(tupleDescriptor->natts <= INDEX_MAX_KEYS);
+
+ for (i = 0; i < tupleDescriptor->natts; i++)
+ {
+ values[i] = index_getattr(tup, i + 1, tupleDescriptor, &isnull[i]);
+ }
+}
+
/*
* Create a palloc'd copy of an index tuple.
*/
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/index/genam.c,v 1.74 2009/06/11 14:48:54 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/access/index/genam.c,v 1.75 2009/08/01 19:59:41 tgl Exp $
*
* NOTES
* many of the old access method routines have been turned into
#include "miscadmin.h"
#include "pgstat.h"
#include "storage/bufmgr.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/tqual.h"
pfree(scan);
}
+/*
+ * ReportUniqueViolation -- Report a unique-constraint violation.
+ *
+ * The index entry represented by values[]/isnull[] violates the unique
+ * constraint enforced by this index. Throw a suitable error.
+ */
+void
+ReportUniqueViolation(Relation indexRelation, Datum *values, bool *isnull)
+{
+ /*
+ * XXX for the moment we use the index's tupdesc as a guide to the
+ * datatypes of the values. This is okay for btree indexes but is in
+ * fact the wrong thing in general. This will have to be fixed if we
+ * are ever to support non-btree unique indexes.
+ */
+ TupleDesc tupdesc = RelationGetDescr(indexRelation);
+ char *key_names;
+ StringInfoData key_values;
+ int i;
+
+ key_names = 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;
+
+ if (isnull[i])
+ val = "null";
+ else
+ {
+ Oid foutoid;
+ bool typisvarlena;
+
+ getTypeOutputInfo(tupdesc->attrs[i]->atttypid,
+ &foutoid, &typisvarlena);
+ val = OidOutputFunctionCall(foutoid, values[i]);
+ }
+
+ if (i > 0)
+ appendStringInfoString(&key_values, ", ");
+ appendStringInfoString(&key_values, 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)));
+}
+
/* ----------------------------------------------------------------
* heap-or-index-scan access to system catalogs
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.171 2009/07/29 20:56:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.172 2009/08/01 19:59:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
}
/*
- * This is a definite conflict.
+ * 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,
+ * which in the worst case might touch this same index
+ * and cause deadlocks.
*/
- ereport(ERROR,
- (errcode(ERRCODE_UNIQUE_VIOLATION),
- errmsg("duplicate key value violates unique constraint \"%s\"",
- RelationGetRelationName(rel))));
+ if (nbuf != InvalidBuffer)
+ _bt_relbuf(rel, nbuf);
+ _bt_relbuf(rel, buf);
+
+ {
+ Datum values[INDEX_MAX_KEYS];
+ bool isnull[INDEX_MAX_KEYS];
+
+ index_deform_tuple(itup, RelationGetDescr(rel),
+ values, isnull);
+ ReportUniqueViolation(rel, values, isnull);
+ }
}
else if (all_dead)
{
*
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.113 2009/06/11 14:49:04 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.114 2009/08/01 19:59:41 tgl Exp $
*
* ----------
*/
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;
+ StringInfoData key_names;
+ StringInfoData key_values;
bool onfk;
int idx,
key_idx;
}
/* 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];
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)
+ if (idx > 0)
{
- sprintf(name_ptr, "...");
- sprintf(val_ptr, "...");
- break;
+ appendStringInfoString(&key_names, ", ");
+ appendStringInfoString(&key_values, ", ");
}
-
- name_ptr += sprintf(name_ptr, "%s%s", idx > 0 ? "," : "", name);
- val_ptr += sprintf(val_ptr, "%s%s", idx > 0 ? "," : "", val);
+ appendStringInfoString(&key_names, name);
+ appendStringInfoString(&key_values, val);
}
if (onfk)
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,
+ key_names.data, key_values.data,
RelationGetRelationName(pk_rel))));
else
ereport(ERROR,
RelationGetRelationName(pk_rel),
constrname, RelationGetRelationName(fk_rel)),
errdetail("Key (%s)=(%s) is still referenced from table \"%s\".",
- key_names, key_values,
+ key_names.data, key_values.data,
RelationGetRelationName(fk_rel))));
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.305 2009/07/29 20:56:19 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.306 2009/08/01 19:59:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static void decompile_column_index_array(Datum column_index_array, Oid relId,
StringInfo buf);
static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
-static char *pg_get_indexdef_worker(Oid indexrelid, int colno, bool showTblSpc,
+static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
+ bool attrsOnly, bool showTblSpc,
int prettyFlags);
static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
int prettyFlags);
Oid indexrelid = PG_GETARG_OID(0);
PG_RETURN_TEXT_P(string_to_text(pg_get_indexdef_worker(indexrelid, 0,
- false, 0)));
+ false, false, 0)));
}
Datum
prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0;
PG_RETURN_TEXT_P(string_to_text(pg_get_indexdef_worker(indexrelid, colno,
- false, prettyFlags)));
+ colno != 0,
+ false,
+ prettyFlags)));
}
/* Internal version that returns a palloc'd C string */
char *
pg_get_indexdef_string(Oid indexrelid)
{
- return pg_get_indexdef_worker(indexrelid, 0, true, 0);
+ return pg_get_indexdef_worker(indexrelid, 0, false, true, 0);
+}
+
+/* Internal version that just reports the column definitions */
+char *
+pg_get_indexdef_columns(Oid indexrelid, bool pretty)
+{
+ int prettyFlags;
+
+ prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0;
+ return pg_get_indexdef_worker(indexrelid, 0, true, false, prettyFlags);
}
static char *
-pg_get_indexdef_worker(Oid indexrelid, int colno, bool showTblSpc,
+pg_get_indexdef_worker(Oid indexrelid, int colno,
+ bool attrsOnly, bool showTblSpc,
int prettyFlags)
{
HeapTuple ht_idx;
*/
initStringInfo(&buf);
- if (!colno)
+ if (!attrsOnly)
appendStringInfo(&buf, "CREATE %sINDEX %s ON %s USING %s (",
idxrec->indisunique ? "UNIQUE " : "",
quote_identifier(NameStr(idxrelrec->relname)),
keycoltype = exprType(indexkey);
}
- /* Provide decoration only in the colno=0 case */
- if (!colno)
+ if (!attrsOnly && (!colno || colno == keyno + 1))
{
/* Add the operator class name, if not default */
get_opclass_name(indclass->values[keyno], keycoltype, &buf);
}
}
- if (!colno)
+ if (!attrsOnly)
{
appendStringInfoChar(&buf, ')');
* 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.79 2009/07/29 20:56:19 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/access/genam.h,v 1.80 2009/08/01 19:59:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern IndexScanDesc RelationGetIndexScan(Relation indexRelation,
int nkeys, ScanKey key);
extern void IndexScanEnd(IndexScanDesc scan);
+extern void ReportUniqueViolation(Relation indexRelation,
+ Datum *values, bool *isnull);
/*
* heap-or-index access to system catalogs (in genam.c)
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/access/itup.h,v 1.51 2009/01/01 17:23:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/access/itup.h,v 1.52 2009/08/01 19:59:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Datum *values, bool *isnull);
extern Datum nocache_index_getattr(IndexTuple tup, int attnum,
TupleDesc tupleDesc, bool *isnull);
+extern void index_deform_tuple(IndexTuple tup, TupleDesc tupleDescriptor,
+ Datum *values, bool *isnull);
extern IndexTuple CopyIndexTuple(IndexTuple source);
#endif /* ITUP_H */
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.335 2009/07/29 20:56:21 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.336 2009/08/01 19:59:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern Datum pg_get_indexdef(PG_FUNCTION_ARGS);
extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS);
extern char *pg_get_indexdef_string(Oid indexrelid);
+extern char *pg_get_indexdef_columns(Oid indexrelid, bool pretty);
extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS);
extern Datum pg_get_constraintdef(PG_FUNCTION_ARGS);
extern Datum pg_get_constraintdef_ext(PG_FUNCTION_ARGS);
[NO_PID]: ecpg_execute on line 31: using PQexec
[NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_check_PQresult on line 31: ERROR: duplicate key value violates unique constraint "test_pkey"
+DETAIL: Key (i)=(7) already exists.
[NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: raising sqlstate 23505 (sqlcode -239) on line 31: duplicate key value violates unique constraint "test_pkey" on line 31
[NO_PID]: sqlca: code: -239, state: 23505
-- 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
-- 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);
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;
-- 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
-- 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);
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;
-- 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}');
-- 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 (textcat(f1, f2))=(ABCDEF) already exists.
-- but this shouldn't:
INSERT INTO func_index_heap VALUES('QWERTY');
--
-- 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 ((f1 || f2))=(ABCDEF) already exists.
-- but this shouldn't:
INSERT INTO func_index_heap VALUES('QWERTY');
--
-- 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"
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 ('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
--
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
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;
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;
-- 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
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 (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 (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;
-- default is immediate so this should fail right away
UPDATE unique_tbl SET i = 1 WHERE i = 0;
ERROR: duplicate key value violates unique constraint "unique_tbl_i_key"
+DETAIL: Key (i)=(1) already exists.
ROLLBACK;
-- check is done at end of statement, so this should succeed
UPDATE unique_tbl SET i = i+1;
INSERT INTO unique_tbl VALUES (3, 'Three'); -- should succeed for now
COMMIT; -- should fail
ERROR: duplicate key value violates unique constraint "unique_tbl_i_key"
+DETAIL: Key (i)=(3) already exists.
-- make constraint check immediate
BEGIN;
SET CONSTRAINTS ALL IMMEDIATE;
INSERT INTO unique_tbl VALUES (3, 'Three'); -- should fail
ERROR: duplicate key value violates unique constraint "unique_tbl_i_key"
+DETAIL: Key (i)=(3) already exists.
COMMIT;
-- forced check when SET CONSTRAINTS is called
BEGIN;
INSERT INTO unique_tbl VALUES (3, 'Three'); -- should succeed for now
SET CONSTRAINTS ALL IMMEDIATE; -- should fail
ERROR: duplicate key value violates unique constraint "unique_tbl_i_key"
+DETAIL: Key (i)=(3) already exists.
COMMIT;
-- test a HOT update that invalidates the conflicting tuple.
-- the trigger should still fire and catch the violation
UPDATE unique_tbl SET t = 'THREE' WHERE i = 3 AND t = 'Three';
COMMIT; -- should fail
ERROR: duplicate key value violates unique constraint "unique_tbl_i_key"
+DETAIL: Key (i)=(3) already exists.
SELECT * FROM unique_tbl;
i | t
---+-------
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
-------