Skip to content

Commit df5d5f1

Browse files
committed
Prevent display of dropped columns in row constraint violation messages.
ExecBuildSlotValueDescription() printed "null" for each dropped column in a row being complained of by ExecConstraints(). This has some sanity in terms of the underlying implementation, but is of course pretty surprising to users. To fix, we must pass the target relation's descriptor to ExecBuildSlotValueDescription(), because the slot descriptor it had been using doesn't get labeled with attisdropped markers. Per bug #8408 from Maxim Boguk. Back-patch to 9.2 where the feature of printing row values in NOT NULL and CHECK constraint violation messages was introduced. Michael Paquier and Tom Lane
1 parent 5d0731d commit df5d5f1

File tree

3 files changed

+54
-12
lines changed

3 files changed

+54
-12
lines changed

src/backend/executor/execMain.c

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ static void ExecutePlan(EState *estate, PlanState *planstate,
8282
static bool ExecCheckRTEPerms(RangeTblEntry *rte);
8383
static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt);
8484
static char *ExecBuildSlotValueDescription(TupleTableSlot *slot,
85+
TupleDesc tupdesc,
8586
int maxfieldlen);
8687
static void EvalPlanQualStart(EPQState *epqstate, EState *parentestate,
8788
Plan *planTree);
@@ -1584,25 +1585,28 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
15841585
TupleTableSlot *slot, EState *estate)
15851586
{
15861587
Relation rel = resultRelInfo->ri_RelationDesc;
1587-
TupleConstr *constr = rel->rd_att->constr;
1588+
TupleDesc tupdesc = RelationGetDescr(rel);
1589+
TupleConstr *constr = tupdesc->constr;
15881590

15891591
Assert(constr);
15901592

15911593
if (constr->has_not_null)
15921594
{
1593-
int natts = rel->rd_att->natts;
1595+
int natts = tupdesc->natts;
15941596
int attrChk;
15951597

15961598
for (attrChk = 1; attrChk <= natts; attrChk++)
15971599
{
1598-
if (rel->rd_att->attrs[attrChk - 1]->attnotnull &&
1600+
if (tupdesc->attrs[attrChk - 1]->attnotnull &&
15991601
slot_attisnull(slot, attrChk))
16001602
ereport(ERROR,
16011603
(errcode(ERRCODE_NOT_NULL_VIOLATION),
16021604
errmsg("null value in column \"%s\" violates not-null constraint",
1603-
NameStr(rel->rd_att->attrs[attrChk - 1]->attname)),
1605+
NameStr(tupdesc->attrs[attrChk - 1]->attname)),
16041606
errdetail("Failing row contains %s.",
1605-
ExecBuildSlotValueDescription(slot, 64)),
1607+
ExecBuildSlotValueDescription(slot,
1608+
tupdesc,
1609+
64)),
16061610
errtablecol(rel, attrChk)));
16071611
}
16081612
}
@@ -1617,7 +1621,9 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
16171621
errmsg("new row for relation \"%s\" violates check constraint \"%s\"",
16181622
RelationGetRelationName(rel), failed),
16191623
errdetail("Failing row contains %s.",
1620-
ExecBuildSlotValueDescription(slot, 64)),
1624+
ExecBuildSlotValueDescription(slot,
1625+
tupdesc,
1626+
64)),
16211627
errtableconstraint(rel, failed)));
16221628
}
16231629
}
@@ -1626,15 +1632,22 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
16261632
* ExecBuildSlotValueDescription -- construct a string representing a tuple
16271633
*
16281634
* This is intentionally very similar to BuildIndexValueDescription, but
1629-
* unlike that function, we truncate long field values. That seems necessary
1630-
* here since heap field values could be very long, whereas index entries
1631-
* typically aren't so wide.
1635+
* unlike that function, we truncate long field values (to at most maxfieldlen
1636+
* bytes). That seems necessary here since heap field values could be very
1637+
* long, whereas index entries typically aren't so wide.
1638+
*
1639+
* Also, unlike the case with index entries, we need to be prepared to ignore
1640+
* dropped columns. We used to use the slot's tuple descriptor to decode the
1641+
* data, but the slot's descriptor doesn't identify dropped columns, so we
1642+
* now need to be passed the relation's descriptor.
16321643
*/
16331644
static char *
1634-
ExecBuildSlotValueDescription(TupleTableSlot *slot, int maxfieldlen)
1645+
ExecBuildSlotValueDescription(TupleTableSlot *slot,
1646+
TupleDesc tupdesc,
1647+
int maxfieldlen)
16351648
{
16361649
StringInfoData buf;
1637-
TupleDesc tupdesc = slot->tts_tupleDescriptor;
1650+
bool write_comma = false;
16381651
int i;
16391652

16401653
/* Make sure the tuple is fully deconstructed */
@@ -1649,6 +1662,10 @@ ExecBuildSlotValueDescription(TupleTableSlot *slot, int maxfieldlen)
16491662
char *val;
16501663
int vallen;
16511664

1665+
/* ignore dropped columns */
1666+
if (tupdesc->attrs[i]->attisdropped)
1667+
continue;
1668+
16521669
if (slot->tts_isnull[i])
16531670
val = "null";
16541671
else
@@ -1661,8 +1678,10 @@ ExecBuildSlotValueDescription(TupleTableSlot *slot, int maxfieldlen)
16611678
val = OidOutputFunctionCall(foutoid, slot->tts_values[i]);
16621679
}
16631680

1664-
if (i > 0)
1681+
if (write_comma)
16651682
appendStringInfoString(&buf, ", ");
1683+
else
1684+
write_comma = true;
16661685

16671686
/* truncate if needed */
16681687
vallen = strlen(val);

src/test/regress/expected/alter_table.out

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1196,6 +1196,20 @@ select * from atacc1;
11961196
--
11971197
(1 row)
11981198

1199+
drop table atacc1;
1200+
-- test constraint error reporting in presence of dropped columns
1201+
create table atacc1 (id serial primary key, value int check (value < 10));
1202+
insert into atacc1(value) values (100);
1203+
ERROR: new row for relation "atacc1" violates check constraint "atacc1_value_check"
1204+
DETAIL: Failing row contains (1, 100).
1205+
alter table atacc1 drop column value;
1206+
alter table atacc1 add column value int check (value < 10);
1207+
insert into atacc1(value) values (100);
1208+
ERROR: new row for relation "atacc1" violates check constraint "atacc1_value_check"
1209+
DETAIL: Failing row contains (2, 100).
1210+
insert into atacc1(id, value) values (null, 0);
1211+
ERROR: null value in column "id" violates not-null constraint
1212+
DETAIL: Failing row contains (null, 0).
11991213
drop table atacc1;
12001214
-- test inheritance
12011215
create table parent (a int, b int, c int);

src/test/regress/sql/alter_table.sql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -874,6 +874,15 @@ select * from atacc1;
874874

875875
drop table atacc1;
876876

877+
-- test constraint error reporting in presence of dropped columns
878+
create table atacc1 (id serial primary key, value int check (value < 10));
879+
insert into atacc1(value) values (100);
880+
alter table atacc1 drop column value;
881+
alter table atacc1 add column value int check (value < 10);
882+
insert into atacc1(value) values (100);
883+
insert into atacc1(id, value) values (null, 0);
884+
drop table atacc1;
885+
877886
-- test inheritance
878887
create table parent (a int, b int, c int);
879888
insert into parent values (1, 2, 3);

0 commit comments

Comments
 (0)