diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index d19e0978e4..a6b26f6668 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -47,6 +47,7 @@ #include "commands/tablespace.h" #include "commands/trigger.h" #include "executor/execdebug.h" +#include "mb/pg_wchar.h" #include "miscadmin.h" #include "optimizer/clauses.h" #include "parser/parse_clause.h" @@ -85,6 +86,8 @@ static void ExecutePlan(EState *estate, PlanState *planstate, DestReceiver *dest); static bool ExecCheckRTEPerms(RangeTblEntry *rte); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); +static char *ExecBuildSlotValueDescription(TupleTableSlot *slot, + int maxfieldlen); static void EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree); static void OpenIntoRel(QueryDesc *queryDesc); @@ -1585,10 +1588,71 @@ ExecConstraints(ResultRelInfo *resultRelInfo, ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("new row for relation \"%s\" violates check constraint \"%s\"", - RelationGetRelationName(rel), failed))); + RelationGetRelationName(rel), failed), + errdetail("Failing row contains %s.", + ExecBuildSlotValueDescription(slot, 64)))); } } +/* + * ExecBuildSlotValueDescription -- construct a string representing a tuple + * + * This is intentionally very similar to BuildIndexValueDescription, but + * unlike that function, we truncate long field values. That seems necessary + * here since heap field values could be very long, whereas index entries + * typically aren't so wide. + */ +static char * +ExecBuildSlotValueDescription(TupleTableSlot *slot, int maxfieldlen) +{ + StringInfoData buf; + TupleDesc tupdesc = slot->tts_tupleDescriptor; + int i; + + /* Make sure the tuple is fully deconstructed */ + slot_getallattrs(slot); + + initStringInfo(&buf); + + appendStringInfoChar(&buf, '('); + + for (i = 0; i < tupdesc->natts; i++) + { + char *val; + int vallen; + + if (slot->tts_isnull[i]) + val = "null"; + else + { + Oid foutoid; + bool typisvarlena; + + getTypeOutputInfo(tupdesc->attrs[i]->atttypid, + &foutoid, &typisvarlena); + val = OidOutputFunctionCall(foutoid, slot->tts_values[i]); + } + + if (i > 0) + appendStringInfoString(&buf, ", "); + + /* truncate if needed */ + vallen = strlen(val); + if (vallen <= maxfieldlen) + appendStringInfoString(&buf, val); + else + { + vallen = pg_mbcliplen(val, vallen, maxfieldlen); + appendBinaryStringInfo(&buf, val, vallen); + appendStringInfoString(&buf, "..."); + } + } + + appendStringInfoChar(&buf, ')'); + + return buf.data; +} + /* * ExecFindRowMark -- find the ExecRowMark struct for given rangetable index diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 1aa4f09ed2..065d8fdcb2 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -390,6 +390,7 @@ alter table atacc1 add constraint atacc_test1 check (test>3); -- should fail insert into atacc1 (test) values (2); ERROR: new row for relation "atacc1" violates check constraint "atacc_test1" +DETAIL: Failing row contains (2). -- should succeed insert into atacc1 (test) values (4); drop table atacc1; @@ -415,6 +416,7 @@ alter table atacc1 add constraint atacc_test1 check (test+test2test); -- should fail for $2 insert into atacc1 (test2, test) values (3, 4); ERROR: new row for relation "atacc1" violates check constraint "atacc1_check" +DETAIL: Failing row contains (4, 3). drop table atacc1; -- inheritance related tests create table atacc1 (test int); @@ -433,10 +436,12 @@ alter table atacc2 add constraint foo check (test2>0); -- fail and then succeed on atacc2 insert into atacc2 (test2) values (-3); ERROR: new row for relation "atacc2" violates check constraint "foo" +DETAIL: Failing row contains (-3). insert into atacc2 (test2) values (3); -- fail and then succeed on atacc3 insert into atacc3 (test2) values (-3); ERROR: new row for relation "atacc3" violates check constraint "foo" +DETAIL: Failing row contains (null, -3, null). insert into atacc3 (test2) values (3); drop table atacc3; drop table atacc2; @@ -507,6 +512,7 @@ insert into atacc1 (test) values (3); -- check constraint is there on child insert into atacc2 (test) values (-3); ERROR: new row for relation "atacc2" violates check constraint "foo" +DETAIL: Failing row contains (-3, null). insert into atacc2 (test) values (3); drop table atacc2; drop table atacc1; @@ -1450,6 +1456,7 @@ NOTICE: merging definition of column "f2" for child "c1" insert into p1 values (1,2,'abc'); insert into c1 values(11,'xyz',33,0); -- should fail ERROR: new row for relation "c1" violates check constraint "p1_a1_check" +DETAIL: Failing row contains (11, xyz, 33, 0). insert into c1 values(11,'xyz',33,22); select * from p1; f1 | a1 | f2 @@ -1537,6 +1544,7 @@ select * from anothertab; insert into anothertab (atcol1, atcol2) values (45, null); -- fails ERROR: new row for relation "anothertab" violates check constraint "anothertab_chk" +DETAIL: Failing row contains (45, null). insert into anothertab (atcol1, atcol2) values (default, null); select * from anothertab; atcol1 | atcol2 @@ -2110,5 +2118,6 @@ ALTER TABLE ONLY test_drop_constr_parent DROP CONSTRAINT "test_drop_constr_paren -- should fail INSERT INTO test_drop_constr_child (c) VALUES (NULL); ERROR: new row for relation "test_drop_constr_child" violates check constraint "test_drop_constr_parent_c_check" +DETAIL: Failing row contains (null). DROP TABLE test_drop_constr_parent CASCADE; NOTICE: drop cascades to table test_drop_constr_child diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out index 521fe01fa1..0e09f898a9 100644 --- a/src/test/regress/expected/domain.out +++ b/src/test/regress/expected/domain.out @@ -199,6 +199,7 @@ insert into nulltest values ('a', 'b', 'c', 'd', NULL); ERROR: domain dcheck does not allow null values insert into nulltest values ('a', 'b', 'c', 'd', 'a'); ERROR: new row for relation "nulltest" violates check constraint "nulltest_col5_check" +DETAIL: Failing row contains (a, b, c, d, a). INSERT INTO nulltest values (NULL, 'b', 'c', 'd', 'd'); ERROR: domain dnotnull does not allow null values INSERT INTO nulltest values ('a', NULL, 'c', 'd', 'c'); @@ -216,6 +217,7 @@ CONTEXT: COPY nulltest, line 1, column col5: null input -- Last row is bad COPY nulltest FROM stdin; ERROR: new row for relation "nulltest" violates check constraint "nulltest_col5_check" +DETAIL: Failing row contains (a, b, c, null, a). CONTEXT: COPY nulltest, line 3: "a b c \N a" select * from nulltest; col1 | col2 | col3 | col4 | col5 diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out index 72986c78a2..e636575205 100644 --- a/src/test/regress/expected/inherit.out +++ b/src/test/regress/expected/inherit.out @@ -640,6 +640,7 @@ INSERT INTO inhg VALUES ('x', 'text', 'y'); /* Succeeds */ INSERT INTO inhg VALUES ('x', 'text', 'y'); /* Succeeds -- Unique constraints not copied */ INSERT INTO inhg VALUES ('x', 'foo', 'y'); /* fails due to constraint */ ERROR: new row for relation "inhg" violates check constraint "foo" +DETAIL: Failing row contains (x, foo, y). SELECT * FROM inhg; /* Two records with three columns in order x=x, xx=text, y=y */ x | xx | y ---+------+--- @@ -721,8 +722,10 @@ select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg insert into ac (aa) values (NULL); ERROR: new row for relation "ac" violates check constraint "ac_check" +DETAIL: Failing row contains (null). insert into bc (aa) values (NULL); ERROR: new row for relation "bc" violates check constraint "ac_check" +DETAIL: Failing row contains (null, null). alter table bc drop constraint ac_check; -- fail, disallowed ERROR: cannot drop inherited constraint "ac_check" of relation "bc" alter table ac drop constraint ac_check; @@ -742,8 +745,10 @@ select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pg insert into ac (aa) values (NULL); ERROR: new row for relation "ac" violates check constraint "ac_aa_check" +DETAIL: Failing row contains (null). insert into bc (aa) values (NULL); ERROR: new row for relation "bc" violates check constraint "ac_aa_check" +DETAIL: Failing row contains (null, null). alter table bc drop constraint ac_aa_check; -- fail, disallowed ERROR: cannot drop inherited constraint "ac_aa_check" of relation "bc" alter table ac drop constraint ac_aa_check; @@ -830,6 +835,7 @@ insert into c1 values(1,1,2); alter table p2 add check (f2>0); insert into c1 values(1,-1,2); -- fail ERROR: new row for relation "c1" violates check constraint "p2_f2_check" +DETAIL: Failing row contains (1, -1, 2). create table c2(f3 int) inherits(p1,p2); \d c2 Table "public.c2" diff --git a/src/test/regress/output/constraints.source b/src/test/regress/output/constraints.source index e2f2939931..9a813535f7 100644 --- a/src/test/regress/output/constraints.source +++ b/src/test/regress/output/constraints.source @@ -68,11 +68,14 @@ INSERT INTO CHECK_TBL VALUES (5); INSERT INTO CHECK_TBL VALUES (4); INSERT INTO CHECK_TBL VALUES (3); ERROR: new row for relation "check_tbl" violates check constraint "check_con" +DETAIL: Failing row contains (3). INSERT INTO CHECK_TBL VALUES (2); ERROR: new row for relation "check_tbl" violates check constraint "check_con" +DETAIL: Failing row contains (2). INSERT INTO CHECK_TBL VALUES (6); INSERT INTO CHECK_TBL VALUES (1); ERROR: new row for relation "check_tbl" violates check constraint "check_con" +DETAIL: Failing row contains (1). SELECT '' AS three, * FROM CHECK_TBL; three | x -------+--- @@ -88,12 +91,16 @@ CREATE TABLE CHECK2_TBL (x int, y text, z int, INSERT INTO CHECK2_TBL VALUES (4, 'check ok', -2); INSERT INTO CHECK2_TBL VALUES (1, 'x check failed', -2); ERROR: new row for relation "check2_tbl" violates check constraint "sequence_con" +DETAIL: Failing row contains (1, x check failed, -2). INSERT INTO CHECK2_TBL VALUES (5, 'z check failed', 10); ERROR: new row for relation "check2_tbl" violates check constraint "sequence_con" +DETAIL: Failing row contains (5, z check failed, 10). INSERT INTO CHECK2_TBL VALUES (0, 'check failed', -2); ERROR: new row for relation "check2_tbl" violates check constraint "sequence_con" +DETAIL: Failing row contains (0, check failed, -2). INSERT INTO CHECK2_TBL VALUES (6, 'check failed', 11); ERROR: new row for relation "check2_tbl" violates check constraint "sequence_con" +DETAIL: Failing row contains (6, check failed, 11). INSERT INTO CHECK2_TBL VALUES (7, 'check ok', 7); SELECT '' AS two, * from CHECK2_TBL; two | x | y | z @@ -113,6 +120,7 @@ CREATE TABLE INSERT_TBL (x INT DEFAULT nextval('insert_seq'), CHECK (x + z = 0)); INSERT INTO INSERT_TBL(x,z) VALUES (2, -2); ERROR: new row for relation "insert_tbl" violates check constraint "insert_con" +DETAIL: Failing row contains (2, -NULL-, -2). SELECT '' AS zero, * FROM INSERT_TBL; zero | x | y | z ------+---+---+--- @@ -126,12 +134,15 @@ SELECT 'one' AS one, nextval('insert_seq'); INSERT INTO INSERT_TBL(y) VALUES ('Y'); ERROR: new row for relation "insert_tbl" violates check constraint "insert_con" +DETAIL: Failing row contains (2, Y, -2). INSERT INTO INSERT_TBL(y) VALUES ('Y'); INSERT INTO INSERT_TBL(x,z) VALUES (1, -2); ERROR: new row for relation "insert_tbl" violates check constraint "insert_tbl_check" +DETAIL: Failing row contains (1, -NULL-, -2). INSERT INTO INSERT_TBL(z,x) VALUES (-7, 7); INSERT INTO INSERT_TBL VALUES (5, 'check failed', -5); ERROR: new row for relation "insert_tbl" violates check constraint "insert_con" +DETAIL: Failing row contains (5, check failed, -5). INSERT INTO INSERT_TBL VALUES (7, '!check failed', -7); INSERT INTO INSERT_TBL(y) VALUES ('-!NULL-'); SELECT '' AS four, * FROM INSERT_TBL; @@ -145,8 +156,10 @@ SELECT '' AS four, * FROM INSERT_TBL; INSERT INTO INSERT_TBL(y,z) VALUES ('check failed', 4); ERROR: new row for relation "insert_tbl" violates check constraint "insert_tbl_check" +DETAIL: Failing row contains (5, check failed, 4). INSERT INTO INSERT_TBL(x,y) VALUES (5, 'check failed'); ERROR: new row for relation "insert_tbl" violates check constraint "insert_con" +DETAIL: Failing row contains (5, check failed, -5). INSERT INTO INSERT_TBL(x,y) VALUES (5, '!check failed'); INSERT INTO INSERT_TBL(y) VALUES ('-!NULL-'); SELECT '' AS six, * FROM INSERT_TBL; @@ -168,6 +181,7 @@ SELECT 'seven' AS one, nextval('insert_seq'); INSERT INTO INSERT_TBL(y) VALUES ('Y'); ERROR: new row for relation "insert_tbl" violates check constraint "insert_con" +DETAIL: Failing row contains (8, Y, -8). SELECT 'eight' AS one, currval('insert_seq'); one | currval -------+--------- @@ -199,10 +213,13 @@ CREATE TABLE INSERT_CHILD (cx INT default 42, INSERT INTO INSERT_CHILD(x,z,cy) VALUES (7,-7,11); INSERT INTO INSERT_CHILD(x,z,cy) VALUES (7,-7,6); ERROR: new row for relation "insert_child" violates check constraint "insert_child_check" +DETAIL: Failing row contains (7, -NULL-, -7, 42, 6). INSERT INTO INSERT_CHILD(x,z,cy) VALUES (6,-7,7); ERROR: new row for relation "insert_child" violates check constraint "insert_tbl_check" +DETAIL: Failing row contains (6, -NULL-, -7, 42, 7). INSERT INTO INSERT_CHILD(x,y,z,cy) VALUES (6,'check failed',-6,7); ERROR: new row for relation "insert_child" violates check constraint "insert_con" +DETAIL: Failing row contains (6, check failed, -6, 42, 7). SELECT * FROM INSERT_CHILD; x | y | z | cx | cy ---+--------+----+----+---- @@ -232,6 +249,7 @@ INSERT INTO INSERT_TBL SELECT * FROM tmp WHERE yd = 'try again'; INSERT INTO INSERT_TBL(y,z) SELECT yd, -7 FROM tmp WHERE yd = 'try again'; INSERT INTO INSERT_TBL(y,z) SELECT yd, -8 FROM tmp WHERE yd = 'try again'; ERROR: new row for relation "insert_tbl" violates check constraint "insert_con" +DETAIL: Failing row contains (8, try again, -8). SELECT '' AS four, * FROM INSERT_TBL; four | x | y | z ------+---+---------------+---- @@ -251,6 +269,7 @@ UPDATE INSERT_TBL SET x = 6 WHERE x = 6; UPDATE INSERT_TBL SET x = -z, z = -x; UPDATE INSERT_TBL SET x = z, z = x; ERROR: new row for relation "insert_tbl" violates check constraint "insert_con" +DETAIL: Failing row contains (-4, Y, 4). SELECT * FROM INSERT_TBL; x | y | z ---+---------------+---- @@ -278,6 +297,7 @@ SELECT '' AS two, * FROM COPY_TBL; COPY COPY_TBL FROM '@abs_srcdir@/data/constrf.data'; ERROR: new row for relation "copy_tbl" violates check constraint "copy_con" +DETAIL: Failing row contains (7, check failed, 6). CONTEXT: COPY copy_tbl, line 2: "7 check failed 6" SELECT * FROM COPY_TBL; x | y | z