@@ -62,6 +62,10 @@ static bool ExecOnConflictUpdate(ModifyTableState *mtstate,
62
62
EState * estate ,
63
63
bool canSetTag ,
64
64
TupleTableSlot * * returning );
65
+ static TupleTableSlot * ExecPrepareTupleRouting (ModifyTableState * mtstate ,
66
+ EState * estate ,
67
+ ResultRelInfo * targetRelInfo ,
68
+ TupleTableSlot * slot );
65
69
66
70
/*
67
71
* Verify that the tuples to be produced by INSERT or UPDATE match the
@@ -259,7 +263,6 @@ ExecInsert(ModifyTableState *mtstate,
259
263
{
260
264
HeapTuple tuple ;
261
265
ResultRelInfo * resultRelInfo ;
262
- ResultRelInfo * saved_resultRelInfo = NULL ;
263
266
Relation resultRelationDesc ;
264
267
Oid newId ;
265
268
List * recheckIndexes = NIL ;
@@ -275,100 +278,6 @@ ExecInsert(ModifyTableState *mtstate,
275
278
* get information on the (current) result relation
276
279
*/
277
280
resultRelInfo = 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
-
372
281
resultRelationDesc = resultRelInfo -> ri_RelationDesc ;
373
282
374
283
/*
@@ -477,7 +386,7 @@ ExecInsert(ModifyTableState *mtstate,
477
386
* No need though if the tuple has been routed, and a BR trigger
478
387
* doesn't exist.
479
388
*/
480
- if (saved_resultRelInfo != NULL &&
389
+ if (resultRelInfo -> ri_PartitionRoot != NULL &&
481
390
!(resultRelInfo -> ri_TrigDesc &&
482
391
resultRelInfo -> ri_TrigDesc -> trig_insert_before_row ))
483
392
check_partition_constr = false;
@@ -645,9 +554,6 @@ ExecInsert(ModifyTableState *mtstate,
645
554
if (resultRelInfo -> ri_projectReturning )
646
555
result = ExecProcessReturning (resultRelInfo , slot , planSlot );
647
556
648
- if (saved_resultRelInfo )
649
- estate -> es_result_relation_info = saved_resultRelInfo ;
650
-
651
557
return result ;
652
558
}
653
559
@@ -1545,6 +1451,111 @@ ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate)
1545
1451
}
1546
1452
}
1547
1453
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
+
1548
1559
/* ----------------------------------------------------------------
1549
1560
* ExecModifyTable
1550
1561
*
@@ -1763,9 +1774,16 @@ ExecModifyTable(PlanState *pstate)
1763
1774
switch (operation )
1764
1775
{
1765
1776
case CMD_INSERT :
1777
+ /* Prepare for tuple routing, if needed. */
1778
+ if (node -> mt_partition_dispatch_info )
1779
+ slot = ExecPrepareTupleRouting (node , estate ,
1780
+ resultRelInfo , slot );
1766
1781
slot = ExecInsert (node , slot , planSlot ,
1767
1782
node -> mt_arbiterindexes , node -> mt_onconflict ,
1768
1783
estate , node -> canSetTag );
1784
+ /* Revert ExecPrepareTupleRouting's node change. */
1785
+ if (node -> mt_partition_dispatch_info )
1786
+ estate -> es_result_relation_info = resultRelInfo ;
1769
1787
break ;
1770
1788
case CMD_UPDATE :
1771
1789
slot = ExecUpdate (node , tupleid , oldtuple , slot , planSlot ,
0 commit comments