static bool ExecCheckRTEPerms(RangeTblEntry *rte);
static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt);
static char *ExecBuildSlotValueDescription(TupleTableSlot *slot,
+ TupleDesc tupdesc,
int maxfieldlen);
static void EvalPlanQualStart(EPQState *epqstate, EState *parentestate,
Plan *planTree);
TupleTableSlot *slot, EState *estate)
{
Relation rel = resultRelInfo->ri_RelationDesc;
- TupleConstr *constr = rel->rd_att->constr;
+ TupleDesc tupdesc = RelationGetDescr(rel);
+ TupleConstr *constr = tupdesc->constr;
Assert(constr);
if (constr->has_not_null)
{
- int natts = rel->rd_att->natts;
+ int natts = tupdesc->natts;
int attrChk;
for (attrChk = 1; attrChk <= natts; attrChk++)
{
- if (rel->rd_att->attrs[attrChk - 1]->attnotnull &&
+ if (tupdesc->attrs[attrChk - 1]->attnotnull &&
slot_attisnull(slot, attrChk))
ereport(ERROR,
(errcode(ERRCODE_NOT_NULL_VIOLATION),
errmsg("null value in column \"%s\" violates not-null constraint",
- NameStr(rel->rd_att->attrs[attrChk - 1]->attname)),
+ NameStr(tupdesc->attrs[attrChk - 1]->attname)),
errdetail("Failing row contains %s.",
- ExecBuildSlotValueDescription(slot, 64))));
+ ExecBuildSlotValueDescription(slot,
+ tupdesc,
+ 64))));
}
}
errmsg("new row for relation \"%s\" violates check constraint \"%s\"",
RelationGetRelationName(rel), failed),
errdetail("Failing row contains %s.",
- ExecBuildSlotValueDescription(slot, 64))));
+ ExecBuildSlotValueDescription(slot,
+ tupdesc,
+ 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.
+ * unlike that function, we truncate long field values (to at most maxfieldlen
+ * bytes). That seems necessary here since heap field values could be very
+ * long, whereas index entries typically aren't so wide.
+ *
+ * Also, unlike the case with index entries, we need to be prepared to ignore
+ * dropped columns. We used to use the slot's tuple descriptor to decode the
+ * data, but the slot's descriptor doesn't identify dropped columns, so we
+ * now need to be passed the relation's descriptor.
*/
static char *
-ExecBuildSlotValueDescription(TupleTableSlot *slot, int maxfieldlen)
+ExecBuildSlotValueDescription(TupleTableSlot *slot,
+ TupleDesc tupdesc,
+ int maxfieldlen)
{
StringInfoData buf;
- TupleDesc tupdesc = slot->tts_tupleDescriptor;
+ bool write_comma = false;
int i;
/* Make sure the tuple is fully deconstructed */
char *val;
int vallen;
+ /* ignore dropped columns */
+ if (tupdesc->attrs[i]->attisdropped)
+ continue;
+
if (slot->tts_isnull[i])
val = "null";
else
val = OidOutputFunctionCall(foutoid, slot->tts_values[i]);
}
- if (i > 0)
+ if (write_comma)
appendStringInfoString(&buf, ", ");
+ else
+ write_comma = true;
/* truncate if needed */
vallen = strlen(val);
--
(1 row)
+drop table atacc1;
+-- test constraint error reporting in presence of dropped columns
+create table atacc1 (id serial primary key, value int check (value < 10));
+NOTICE: CREATE TABLE will create implicit sequence "atacc1_id_seq" for serial column "atacc1.id"
+NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "atacc1_pkey" for table "atacc1"
+insert into atacc1(value) values (100);
+ERROR: new row for relation "atacc1" violates check constraint "atacc1_value_check"
+DETAIL: Failing row contains (1, 100).
+alter table atacc1 drop column value;
+alter table atacc1 add column value int check (value < 10);
+insert into atacc1(value) values (100);
+ERROR: new row for relation "atacc1" violates check constraint "atacc1_value_check"
+DETAIL: Failing row contains (2, 100).
+insert into atacc1(id, value) values (null, 0);
+ERROR: null value in column "id" violates not-null constraint
+DETAIL: Failing row contains (null, 0).
drop table atacc1;
-- test inheritance
create table parent (a int, b int, c int);