@@ -62,6 +62,10 @@ static bool ExecOnConflictUpdate(ModifyTableState *mtstate,
6262EState * estate ,
6363bool canSetTag ,
6464TupleTableSlot * * returning );
65+ static TupleTableSlot * ExecPrepareTupleRouting (ModifyTableState * mtstate ,
66+ EState * estate ,
67+ ResultRelInfo * targetRelInfo ,
68+ TupleTableSlot * slot );
6569
6670/*
6771 * Verify that the tuples to be produced by INSERT or UPDATE match the
@@ -259,7 +263,6 @@ ExecInsert(ModifyTableState *mtstate,
259263{
260264HeapTuple tuple ;
261265ResultRelInfo * resultRelInfo ;
262- ResultRelInfo * saved_resultRelInfo = NULL ;
263266Relation resultRelationDesc ;
264267Oid newId ;
265268List * recheckIndexes = NIL ;
@@ -275,100 +278,6 @@ ExecInsert(ModifyTableState *mtstate,
275278 * get information on the (current) result relation
276279 */
277280resultRelInfo = estate -> es_result_relation_info ;
278-
279- /* Determine the partition to heap_insert the tuple into */
280- if (mtstate -> mt_partition_dispatch_info )
281- {
282- int leaf_part_index ;
283- TupleConversionMap * map ;
284-
285- /*
286- * Away we go ... If we end up not finding a partition after all,
287- * ExecFindPartition() does not return and errors out instead.
288- * Otherwise, the returned value is to be used as an index into arrays
289- * mt_partitions[] and mt_partition_tupconv_maps[] that will get us
290- * the ResultRelInfo and TupleConversionMap for the partition,
291- * respectively.
292- */
293- leaf_part_index = ExecFindPartition (resultRelInfo ,
294- mtstate -> mt_partition_dispatch_info ,
295- slot ,
296- estate );
297- Assert (leaf_part_index >=0 &&
298- leaf_part_index < mtstate -> mt_num_partitions );
299-
300- /*
301- * Save the old ResultRelInfo and switch to the one corresponding to
302- * the selected partition.
303- */
304- saved_resultRelInfo = resultRelInfo ;
305- resultRelInfo = mtstate -> mt_partitions + leaf_part_index ;
306-
307- /* We do not yet have a way to insert into a foreign partition */
308- if (resultRelInfo -> ri_FdwRoutine )
309- ereport (ERROR ,
310- (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
311- errmsg ("cannot route inserted tuples to a foreign table" )));
312-
313- /* For ExecInsertIndexTuples() to work on the partition's indexes */
314- estate -> es_result_relation_info = resultRelInfo ;
315-
316- /*
317- * If we're capturing transition tuples, we might need to convert from
318- * the partition rowtype to parent rowtype.
319- */
320- if (mtstate -> mt_transition_capture != NULL )
321- {
322- if (resultRelInfo -> ri_TrigDesc &&
323- (resultRelInfo -> ri_TrigDesc -> trig_insert_before_row ||
324- resultRelInfo -> ri_TrigDesc -> trig_insert_instead_row ))
325- {
326- /*
327- * If there are any BEFORE or INSTEAD triggers on the
328- * partition, we'll have to be ready to convert their result
329- * back to tuplestore format.
330- */
331- mtstate -> mt_transition_capture -> tcs_original_insert_tuple = NULL ;
332- mtstate -> mt_transition_capture -> tcs_map =
333- mtstate -> mt_transition_tupconv_maps [leaf_part_index ];
334- }
335- else
336- {
337- /*
338- * Otherwise, just remember the original unconverted tuple, to
339- * avoid a needless round trip conversion.
340- */
341- mtstate -> mt_transition_capture -> tcs_original_insert_tuple = tuple ;
342- mtstate -> mt_transition_capture -> tcs_map = NULL ;
343- }
344- }
345- if (mtstate -> mt_oc_transition_capture != NULL )
346- mtstate -> mt_oc_transition_capture -> tcs_map =
347- mtstate -> mt_transition_tupconv_maps [leaf_part_index ];
348-
349- /*
350- * We might need to convert from the parent rowtype to the partition
351- * rowtype.
352- */
353- map = mtstate -> mt_partition_tupconv_maps [leaf_part_index ];
354- if (map )
355- {
356- Relation partrel = resultRelInfo -> ri_RelationDesc ;
357-
358- tuple = do_convert_tuple (tuple ,map );
359-
360- /*
361- * We must use the partition's tuple descriptor from this point
362- * on, until we're finished dealing with the partition. Use the
363- * dedicated slot for that.
364- */
365- slot = mtstate -> mt_partition_tuple_slot ;
366- Assert (slot != NULL );
367- ExecSetSlotDescriptor (slot ,RelationGetDescr (partrel ));
368- ExecStoreTuple (tuple ,slot ,InvalidBuffer , true);
369- }
370- }
371-
372281resultRelationDesc = resultRelInfo -> ri_RelationDesc ;
373282
374283/*
@@ -477,7 +386,7 @@ ExecInsert(ModifyTableState *mtstate,
477386 * No need though if the tuple has been routed, and a BR trigger
478387 * doesn't exist.
479388 */
480- if (saved_resultRelInfo != NULL &&
389+ if (resultRelInfo -> ri_PartitionRoot != NULL &&
481390!(resultRelInfo -> ri_TrigDesc &&
482391resultRelInfo -> ri_TrigDesc -> trig_insert_before_row ))
483392check_partition_constr = false;
@@ -645,9 +554,6 @@ ExecInsert(ModifyTableState *mtstate,
645554if (resultRelInfo -> ri_projectReturning )
646555result = ExecProcessReturning (resultRelInfo ,slot ,planSlot );
647556
648- if (saved_resultRelInfo )
649- estate -> es_result_relation_info = saved_resultRelInfo ;
650-
651557return result ;
652558}
653559
@@ -1545,6 +1451,111 @@ ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate)
15451451}
15461452}
15471453
1454+ /*
1455+ * ExecPrepareTupleRouting --- prepare for routing one tuple
1456+ *
1457+ * Determine the partition in which the tuple in slot is to be inserted,
1458+ * and modify mtstate and estate to prepare for it.
1459+ *
1460+ * Caller must revert the estate changes after executing the insertion!
1461+ * In mtstate, transition capture changes may also need to be reverted.
1462+ *
1463+ * Returns a slot holding the tuple of the partition rowtype.
1464+ */
1465+ static TupleTableSlot *
1466+ ExecPrepareTupleRouting (ModifyTableState * mtstate ,
1467+ EState * estate ,
1468+ ResultRelInfo * targetRelInfo ,
1469+ TupleTableSlot * slot )
1470+ {
1471+ int partidx ;
1472+ ResultRelInfo * partrel ;
1473+ HeapTuple tuple ;
1474+ TupleConversionMap * map ;
1475+
1476+ /*
1477+ * Determine the target partition. If ExecFindPartition does not find
1478+ * a partition after all, it doesn't return here; otherwise, the returned
1479+ * value is to be used as an index into the arrays for the resultRelInfo
1480+ * and TupleConversionMap for the partition.
1481+ */
1482+ partidx = ExecFindPartition (targetRelInfo ,
1483+ mtstate -> mt_partition_dispatch_info ,
1484+ slot ,
1485+ estate );
1486+ Assert (partidx >=0 && partidx < mtstate -> mt_num_partitions );
1487+
1488+ /* Get the ResultRelInfo corresponding to the selected partition. */
1489+ partrel = & mtstate -> mt_partitions [partidx ];
1490+
1491+ /* We do not yet have a way to insert into a foreign partition */
1492+ if (partrel -> ri_FdwRoutine )
1493+ ereport (ERROR ,
1494+ (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
1495+ errmsg ("cannot route inserted tuples to a foreign table" )));
1496+
1497+ /*
1498+ * Make it look like we are inserting into the partition.
1499+ */
1500+ estate -> es_result_relation_info = partrel ;
1501+
1502+ /* Get the heap tuple out of the given slot. */
1503+ tuple = ExecMaterializeSlot (slot );
1504+
1505+ /*
1506+ * If we're capturing transition tuples, we might need to convert from the
1507+ * partition rowtype to parent rowtype.
1508+ */
1509+ if (mtstate -> mt_transition_capture != NULL )
1510+ {
1511+ if (partrel -> ri_TrigDesc &&
1512+ partrel -> ri_TrigDesc -> trig_insert_before_row )
1513+ {
1514+ /*
1515+ * If there are any BEFORE triggers on the partition, we'll have
1516+ * to be ready to convert their result back to tuplestore format.
1517+ */
1518+ mtstate -> mt_transition_capture -> tcs_original_insert_tuple = NULL ;
1519+ mtstate -> mt_transition_capture -> tcs_map =
1520+ mtstate -> mt_transition_tupconv_maps [partidx ];
1521+ }
1522+ else
1523+ {
1524+ /*
1525+ * Otherwise, just remember the original unconverted tuple, to
1526+ * avoid a needless round trip conversion.
1527+ */
1528+ mtstate -> mt_transition_capture -> tcs_original_insert_tuple = tuple ;
1529+ mtstate -> mt_transition_capture -> tcs_map = NULL ;
1530+ }
1531+ }
1532+ if (mtstate -> mt_oc_transition_capture != NULL )
1533+ {
1534+ mtstate -> mt_oc_transition_capture -> tcs_map =
1535+ mtstate -> mt_transition_tupconv_maps [partidx ];
1536+ }
1537+
1538+ /*
1539+ * Convert the tuple, if necessary.
1540+ */
1541+ map = mtstate -> mt_partition_tupconv_maps [partidx ];
1542+ if (map )
1543+ {
1544+ tuple = do_convert_tuple (tuple ,map );
1545+
1546+ /*
1547+ * We must use the partition's tuple descriptor from this point on,
1548+ * until we're finished dealing with the partition. Use the
1549+ * dedicated slot for that.
1550+ */
1551+ slot = mtstate -> mt_partition_tuple_slot ;
1552+ ExecSetSlotDescriptor (slot ,RelationGetDescr (partrel -> ri_RelationDesc ));
1553+ ExecStoreTuple (tuple ,slot ,InvalidBuffer , true);
1554+ }
1555+
1556+ return slot ;
1557+ }
1558+
15481559/* ----------------------------------------------------------------
15491560 * ExecModifyTable
15501561 *
@@ -1763,9 +1774,16 @@ ExecModifyTable(PlanState *pstate)
17631774switch (operation )
17641775{
17651776case CMD_INSERT :
1777+ /* Prepare for tuple routing, if needed. */
1778+ if (node -> mt_partition_dispatch_info )
1779+ slot = ExecPrepareTupleRouting (node ,estate ,
1780+ resultRelInfo ,slot );
17661781slot = ExecInsert (node ,slot ,planSlot ,
17671782node -> mt_arbiterindexes ,node -> mt_onconflict ,
17681783estate ,node -> canSetTag );
1784+ /* Revert ExecPrepareTupleRouting's node change. */
1785+ if (node -> mt_partition_dispatch_info )
1786+ estate -> es_result_relation_info = resultRelInfo ;
17691787break ;
17701788case CMD_UPDATE :
17711789slot = ExecUpdate (node ,tupleid ,oldtuple ,slot ,planSlot ,