@@ -828,6 +828,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
828828resultRelation ,
829829resultRelationIndex ,
830830 true,
831+ NULL ,
831832estate -> es_instrument );
832833resultRelInfo ++ ;
833834}
@@ -1218,6 +1219,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
12181219Relation resultRelationDesc ,
12191220Index resultRelationIndex ,
12201221bool load_partition_check ,
1222+ Relation partition_root ,
12211223int instrument_options )
12221224{
12231225MemSet (resultRelInfo ,0 ,sizeof (ResultRelInfo ));
@@ -1259,6 +1261,11 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
12591261resultRelInfo -> ri_PartitionCheck =
12601262RelationGetPartitionQual (resultRelationDesc ,
12611263 true);
1264+ /*
1265+ * The following gets set to NULL unless we are initializing leaf
1266+ * partitions for tuple-routing.
1267+ */
1268+ resultRelInfo -> ri_PartitionRoot = partition_root ;
12621269}
12631270
12641271/*
@@ -1322,6 +1329,7 @@ ExecGetTriggerResultRel(EState *estate, Oid relid)
13221329rel ,
132313300 ,/* dummy rangetable index */
13241331 true,
1332+ NULL ,
13251333estate -> es_instrument );
13261334estate -> es_trig_target_relations =
13271335lappend (estate -> es_trig_target_relations ,rInfo );
@@ -1743,9 +1751,21 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
17431751return ExecQual (resultRelInfo -> ri_PartitionCheckExpr ,econtext , true);
17441752}
17451753
1754+ /*
1755+ * ExecConstraints - check constraints of the tuple in 'slot'
1756+ *
1757+ * This checks the traditional NOT NULL and check constraints, as well as
1758+ * the partition constraint, if any.
1759+ *
1760+ * Note: 'slot' contains the tuple to check the constraints of, which may
1761+ * have been converted from the original input tuple after tuple routing,
1762+ * while 'orig_slot' contains the original tuple to be shown in the message,
1763+ * if an error occurs.
1764+ */
17461765void
17471766ExecConstraints (ResultRelInfo * resultRelInfo ,
1748- TupleTableSlot * slot ,EState * estate )
1767+ TupleTableSlot * slot ,TupleTableSlot * orig_slot ,
1768+ EState * estate )
17491769{
17501770Relation rel = resultRelInfo -> ri_RelationDesc ;
17511771TupleDesc tupdesc = RelationGetDescr (rel );
@@ -1767,22 +1787,34 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
17671787slot_attisnull (slot ,attrChk ))
17681788{
17691789char * val_desc ;
1790+ Relation orig_rel = rel ;
1791+ TupleDesc orig_tupdesc = tupdesc ;
1792+
1793+ /*
1794+ * choose the correct relation to build val_desc from the
1795+ * tuple contained in orig_slot
1796+ */
1797+ if (resultRelInfo -> ri_PartitionRoot )
1798+ {
1799+ rel = resultRelInfo -> ri_PartitionRoot ;
1800+ tupdesc = RelationGetDescr (rel );
1801+ }
17701802
17711803insertedCols = GetInsertedColumns (resultRelInfo ,estate );
17721804updatedCols = GetUpdatedColumns (resultRelInfo ,estate );
17731805modifiedCols = bms_union (insertedCols ,updatedCols );
17741806val_desc = ExecBuildSlotValueDescription (RelationGetRelid (rel ),
1775- slot ,
1807+ orig_slot ,
17761808tupdesc ,
17771809modifiedCols ,
1778181064 );
17791811
17801812ereport (ERROR ,
17811813(errcode (ERRCODE_NOT_NULL_VIOLATION ),
17821814errmsg ("null value in column \"%s\" violates not-null constraint" ,
1783- NameStr (tupdesc -> attrs [attrChk - 1 ]-> attname )),
1815+ NameStr (orig_tupdesc -> attrs [attrChk - 1 ]-> attname )),
17841816val_desc ?errdetail ("Failing row contains %s." ,val_desc ) :0 ,
1785- errtablecol (rel ,attrChk )));
1817+ errtablecol (orig_rel ,attrChk )));
17861818}
17871819}
17881820}
@@ -1794,41 +1826,57 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
17941826if ((failed = ExecRelCheck (resultRelInfo ,slot ,estate ))!= NULL )
17951827{
17961828char * val_desc ;
1829+ Relation orig_rel = rel ;
1830+
1831+ /* See the comment above. */
1832+ if (resultRelInfo -> ri_PartitionRoot )
1833+ {
1834+ rel = resultRelInfo -> ri_PartitionRoot ;
1835+ tupdesc = RelationGetDescr (rel );
1836+ }
17971837
17981838insertedCols = GetInsertedColumns (resultRelInfo ,estate );
17991839updatedCols = GetUpdatedColumns (resultRelInfo ,estate );
18001840modifiedCols = bms_union (insertedCols ,updatedCols );
18011841val_desc = ExecBuildSlotValueDescription (RelationGetRelid (rel ),
1802- slot ,
1842+ orig_slot ,
18031843tupdesc ,
18041844modifiedCols ,
1805184564 );
18061846ereport (ERROR ,
18071847(errcode (ERRCODE_CHECK_VIOLATION ),
18081848errmsg ("new row for relation \"%s\" violates check constraint \"%s\"" ,
1809- RelationGetRelationName (rel ),failed ),
1849+ RelationGetRelationName (orig_rel ),failed ),
18101850val_desc ?errdetail ("Failing row contains %s." ,val_desc ) :0 ,
1811- errtableconstraint (rel ,failed )));
1851+ errtableconstraint (orig_rel ,failed )));
18121852}
18131853}
18141854
18151855if (resultRelInfo -> ri_PartitionCheck &&
18161856!ExecPartitionCheck (resultRelInfo ,slot ,estate ))
18171857{
18181858char * val_desc ;
1859+ Relation orig_rel = rel ;
1860+
1861+ /* See the comment above. */
1862+ if (resultRelInfo -> ri_PartitionRoot )
1863+ {
1864+ rel = resultRelInfo -> ri_PartitionRoot ;
1865+ tupdesc = RelationGetDescr (rel );
1866+ }
18191867
18201868insertedCols = GetInsertedColumns (resultRelInfo ,estate );
18211869updatedCols = GetUpdatedColumns (resultRelInfo ,estate );
18221870modifiedCols = bms_union (insertedCols ,updatedCols );
18231871val_desc = ExecBuildSlotValueDescription (RelationGetRelid (rel ),
1824- slot ,
1872+ orig_slot ,
18251873tupdesc ,
18261874modifiedCols ,
1827187564 );
18281876ereport (ERROR ,
18291877(errcode (ERRCODE_CHECK_VIOLATION ),
18301878errmsg ("new row for relation \"%s\" violates partition constraint" ,
1831- RelationGetRelationName (rel )),
1879+ RelationGetRelationName (orig_rel )),
18321880val_desc ?errdetail ("Failing row contains %s." ,val_desc ) :0 ));
18331881}
18341882}
@@ -3086,6 +3134,7 @@ ExecSetupPartitionTupleRouting(Relation rel,
30863134partrel ,
308731351 ,/* dummy */
30883136 false,
3137+ rel ,
308931380 );
30903139
30913140/*