diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index bd2ea25..df1ea35 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -24,6 +24,7 @@
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
+#include "optimizer/optimizer.h"
 #include "partitioning/partbounds.h"
 #include "partitioning/partdesc.h"
 #include "partitioning/partprune.h"
@@ -733,6 +734,40 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
 						leaf_part_rri, partidx);
 
 	/*
+	 * If RETURNING will access partition's system columns, which the root
+	 * partitioned table's virtual tuple slot will not be able to provide,
+	 * create a partition-specific slot that will into which the tuple being
+	 * inserted will be copied.  ExecInitRoutingInfo() may already have set
+	 * one up for tuple conversion, in which case nothing to do.
+	 */
+	if (leaf_part_rri->ri_returningList &&
+		leaf_part_rri->ri_PartitionInfo->pi_PartitionTupleSlot == NULL)
+	{
+		int			firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
+		int			col;
+		Bitmapset  *returning_attrs = NULL;
+		bool		returning_has_syscol = false;
+
+		pull_varattnos((Node *) leaf_part_rri->ri_returningList, firstVarno,
+					   &returning_attrs);
+		col = -1;
+		while ((col = bms_next_member(returning_attrs, col)) >= 0)
+		{
+			AttrNumber  attno = col + FirstLowInvalidHeapAttributeNumber;
+
+			if (attno < 0)
+			{
+				returning_has_syscol = true;
+				break;
+			}
+		}
+
+		if (returning_has_syscol)
+			leaf_part_rri->ri_PartitionInfo->pi_PartitionTupleSlot =
+				table_slot_create(partrel, &estate->es_tupleTable);
+	}
+
+	/*
 	 * If there is an ON CONFLICT clause, initialize state for it.
 	 */
 	if (node && node->onConflictAction != ONCONFLICT_NONE)
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 20a4c47..12445c2 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1940,7 +1940,8 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate,
 	}
 
 	/*
-	 * Convert the tuple, if necessary.
+	 * Convert the tuple, if necessary, and store in the partition-specific
+	 * slot, if any.
 	 */
 	map = partrouteinfo->pi_RootToPartitionMap;
 	if (map != NULL)
@@ -1949,6 +1950,12 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate,
 
 		slot = execute_attr_map_slot(map->attrMap, slot, new_slot);
 	}
+	else if (partrouteinfo->pi_PartitionTupleSlot)
+	{
+		TupleTableSlot *new_slot = partrouteinfo->pi_PartitionTupleSlot;
+
+		slot = ExecCopySlot(new_slot, slot);
+	}
 
 	return slot;
 }
diff --git a/src/test/regress/expected/insert.out b/src/test/regress/expected/insert.out
index eb9d45b..b249662 100644
--- a/src/test/regress/expected/insert.out
+++ b/src/test/regress/expected/insert.out
@@ -962,15 +962,17 @@ select tableoid::regclass, * from mcrparted order by a, b;
 (11 rows)
 
 drop table mcrparted;
--- check that wholerow vars in the RETURNING list work with partitioned tables
+-- check that wholerow and system columns in the RETURNING list work with partitioned tables
 create table returningwrtest (a int) partition by list (a);
 create table returningwrtest1 partition of returningwrtest for values in (1);
-insert into returningwrtest values (1) returning returningwrtest;
- returningwrtest 
------------------
- (1)
+CREATE FUNCTION xid_current() RETURNS xid LANGUAGE SQL AS $$SELECT (txid_current() % ((1::int8<<32)))::text::xid;$$;
+insert into returningwrtest values (1) returning returningwrtest, ctid, xmin = xid_current() AS xmin_correct;
+ returningwrtest | ctid  | xmin_correct 
+-----------------+-------+--------------
+ (1)             | (0,1) | t
 (1 row)
 
+DROP FUNCTION xid_current();
 -- check also that the wholerow vars in RETURNING list are converted as needed
 alter table returningwrtest add b text;
 create table returningwrtest2 (b text, c int, a int);
diff --git a/src/test/regress/sql/insert.sql b/src/test/regress/sql/insert.sql
index ffd4aac..f966863 100644
--- a/src/test/regress/sql/insert.sql
+++ b/src/test/regress/sql/insert.sql
@@ -592,10 +592,12 @@ insert into mcrparted values ('aaa', 0), ('b', 0), ('bz', 10), ('c', -10),
 select tableoid::regclass, * from mcrparted order by a, b;
 drop table mcrparted;
 
--- check that wholerow vars in the RETURNING list work with partitioned tables
+-- check that wholerow and system columns in the RETURNING list work with partitioned tables
 create table returningwrtest (a int) partition by list (a);
 create table returningwrtest1 partition of returningwrtest for values in (1);
-insert into returningwrtest values (1) returning returningwrtest;
+CREATE FUNCTION xid_current() RETURNS xid LANGUAGE SQL AS $$SELECT (txid_current() % ((1::int8<<32)))::text::xid;$$;
+insert into returningwrtest values (1) returning returningwrtest, ctid, xmin = xid_current() AS xmin_correct;
+DROP FUNCTION xid_current();
 
 -- check also that the wholerow vars in RETURNING list are converted as needed
 alter table returningwrtest add b text;
