Skip to content

Commit 8bd5a6a

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 aa8a2c3 commit 8bd5a6a

File tree

3 files changed

+56
-12
lines changed

3 files changed

+56
-12
lines changed

src/backend/executor/execMain.c

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ static void ExecutePlan(EState *estate, PlanState *planstate,
8080
static bool ExecCheckRTEPerms(RangeTblEntry *rte);
8181
static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt);
8282
static char *ExecBuildSlotValueDescription(TupleTableSlot *slot,
83+
TupleDesc tupdesc,
8384
int maxfieldlen);
8485
static void EvalPlanQualStart(EPQState *epqstate, EState *parentestate,
8586
Plan *planTree);
@@ -1506,25 +1507,28 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
15061507
TupleTableSlot *slot, EState *estate)
15071508
{
15081509
Relation rel = resultRelInfo->ri_RelationDesc;
1509-
TupleConstr *constr = rel->rd_att->constr;
1510+
TupleDesc tupdesc = RelationGetDescr(rel);
1511+
TupleConstr *constr = tupdesc->constr;
15101512

15111513
Assert(constr);
15121514

15131515
if (constr->has_not_null)
15141516
{
1515-
int natts = rel->rd_att->natts;
1517+
int natts = tupdesc->natts;
15161518
int attrChk;
15171519

15181520
for (attrChk = 1; attrChk <= natts; attrChk++)
15191521
{
1520-
if (rel->rd_att->attrs[attrChk - 1]->attnotnull &&
1522+
if (tupdesc->attrs[attrChk - 1]->attnotnull &&
15211523
slot_attisnull(slot, attrChk))
15221524
ereport(ERROR,
15231525
(errcode(ERRCODE_NOT_NULL_VIOLATION),
15241526
errmsg("null value in column \"%s\" violates not-null constraint",
1525-
NameStr(rel->rd_att->attrs[attrChk - 1]->attname)),
1527+
NameStr(tupdesc->attrs[attrChk - 1]->attname)),
15261528
errdetail("Failing row contains %s.",
1527-
ExecBuildSlotValueDescription(slot, 64))));
1529+
ExecBuildSlotValueDescription(slot,
1530+
tupdesc,
1531+
64))));
15281532
}
15291533
}
15301534

@@ -1538,23 +1542,32 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
15381542
errmsg("new row for relation \"%s\" violates check constraint \"%s\"",
15391543
RelationGetRelationName(rel), failed),
15401544
errdetail("Failing row contains %s.",
1541-
ExecBuildSlotValueDescription(slot, 64))));
1545+
ExecBuildSlotValueDescription(slot,
1546+
tupdesc,
1547+
64))));
15421548
}
15431549
}
15441550

15451551
/*
15461552
* ExecBuildSlotValueDescription -- construct a string representing a tuple
15471553
*
15481554
* This is intentionally very similar to BuildIndexValueDescription, but
1549-
* unlike that function, we truncate long field values. That seems necessary
1550-
* here since heap field values could be very long, whereas index entries
1551-
* typically aren't so wide.
1555+
* unlike that function, we truncate long field values (to at most maxfieldlen
1556+
* bytes). That seems necessary here since heap field values could be very
1557+
* long, whereas index entries typically aren't so wide.
1558+
*
1559+
* Also, unlike the case with index entries, we need to be prepared to ignore
1560+
* dropped columns. We used to use the slot's tuple descriptor to decode the
1561+
* data, but the slot's descriptor doesn't identify dropped columns, so we
1562+
* now need to be passed the relation's descriptor.
15521563
*/
15531564
static char *
1554-
ExecBuildSlotValueDescription(TupleTableSlot *slot, int maxfieldlen)
1565+
ExecBuildSlotValueDescription(TupleTableSlot *slot,
1566+
TupleDesc tupdesc,
1567+
int maxfieldlen)
15551568
{
15561569
StringInfoData buf;
1557-
TupleDesc tupdesc = slot->tts_tupleDescriptor;
1570+
bool write_comma = false;
15581571
int i;
15591572

15601573
/* Make sure the tuple is fully deconstructed */
@@ -1569,6 +1582,10 @@ ExecBuildSlotValueDescription(TupleTableSlot *slot, int maxfieldlen)
15691582
char *val;
15701583
int vallen;
15711584

1585+
/* ignore dropped columns */
1586+
if (tupdesc->attrs[i]->attisdropped)
1587+
continue;
1588+
15721589
if (slot->tts_isnull[i])
15731590
val = "null";
15741591
else
@@ -1581,8 +1598,10 @@ ExecBuildSlotValueDescription(TupleTableSlot *slot, int maxfieldlen)
15811598
val = OidOutputFunctionCall(foutoid, slot->tts_values[i]);
15821599
}
15831600

1584-
if (i > 0)
1601+
if (write_comma)
15851602
appendStringInfoString(&buf, ", ");
1603+
else
1604+
write_comma = true;
15861605

15871606
/* truncate if needed */
15881607
vallen = strlen(val);

src/test/regress/expected/alter_table.out

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,6 +1223,22 @@ select * from atacc1;
12231223
--
12241224
(1 row)
12251225

1226+
drop table atacc1;
1227+
-- test constraint error reporting in presence of dropped columns
1228+
create table atacc1 (id serial primary key, value int check (value < 10));
1229+
NOTICE: CREATE TABLE will create implicit sequence "atacc1_id_seq" for serial column "atacc1.id"
1230+
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "atacc1_pkey" for table "atacc1"
1231+
insert into atacc1(value) values (100);
1232+
ERROR: new row for relation "atacc1" violates check constraint "atacc1_value_check"
1233+
DETAIL: Failing row contains (1, 100).
1234+
alter table atacc1 drop column value;
1235+
alter table atacc1 add column value int check (value < 10);
1236+
insert into atacc1(value) values (100);
1237+
ERROR: new row for relation "atacc1" violates check constraint "atacc1_value_check"
1238+
DETAIL: Failing row contains (2, 100).
1239+
insert into atacc1(id, value) values (null, 0);
1240+
ERROR: null value in column "id" violates not-null constraint
1241+
DETAIL: Failing row contains (null, 0).
12261242
drop table atacc1;
12271243
-- test inheritance
12281244
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)