@@ -239,13 +239,9 @@ fini_result_parts_storage(ResultPartsStorage *parts_storage)
239
239
}
240
240
241
241
/* Free conversion-related stuff */
242
- if (rri_holder -> tuple_map )
243
- {
244
- FreeTupleDesc (rri_holder -> tuple_map -> indesc );
245
- FreeTupleDesc (rri_holder -> tuple_map -> outdesc );
242
+ destroy_tuple_map (rri_holder -> tuple_map );
246
243
247
- free_conversion_map (rri_holder -> tuple_map );
248
- }
244
+ destroy_tuple_map (rri_holder -> tuple_map_child );
249
245
250
246
/* Don't forget to close 'prel'! */
251
247
if (rri_holder -> prel )
@@ -381,6 +377,13 @@ scan_result_parts_storage(ResultPartsStorage *parts_storage, Oid partid)
381
377
*/
382
378
rri_holder -> tuple_map = build_part_tuple_map (base_rel , child_rel );
383
379
380
+ /*
381
+ * Field for child->child tuple transformation map. We need to
382
+ * convert tuples because child TupleDesc might have extra
383
+ * columns ('ctid' etc.) and need remove them.
384
+ */
385
+ rri_holder -> tuple_map_child = NULL ;
386
+
384
387
/* Default values */
385
388
rri_holder -> prel = NULL ;
386
389
rri_holder -> prel_expr_state = NULL ;
@@ -468,6 +471,73 @@ build_part_tuple_map(Relation base_rel, Relation child_rel)
468
471
return tuple_map ;
469
472
}
470
473
474
+ /*
475
+ * Build tuple conversion map (e.g. partition tuple has extra column(s)).
476
+ * We create a special tuple map (tuple_map_child), which, when applied to the
477
+ * tuple of partition, translates the tuple attributes into the tuple
478
+ * attributes of the same partition, discarding service attributes like "ctid"
479
+ * (i.e. working like junkFilter).
480
+ */
481
+ TupleConversionMap *
482
+ build_part_tuple_map_child (Relation child_rel )
483
+ {
484
+ TupleConversionMap * tuple_map ;
485
+ TupleDesc child_tupdesc1 ;
486
+ TupleDesc child_tupdesc2 ;
487
+ int n ;
488
+ #if PG_VERSION_NUM >= 130000
489
+ AttrMap * attrMap ;
490
+ #else
491
+ AttrNumber * attrMap ;
492
+ #endif
493
+
494
+ child_tupdesc1 = CreateTupleDescCopy (RelationGetDescr (child_rel ));
495
+ child_tupdesc1 -> tdtypeid = InvalidOid ;
496
+
497
+ child_tupdesc2 = CreateTupleDescCopy (RelationGetDescr (child_rel ));
498
+ child_tupdesc2 -> tdtypeid = InvalidOid ;
499
+
500
+ /* Generate tuple transformation map */
501
+ #if PG_VERSION_NUM >= 130000
502
+ attrMap = build_attrmap_by_name (child_tupdesc1 , child_tupdesc2 );
503
+ #else
504
+ attrMap = convert_tuples_by_name_map (child_tupdesc1 , child_tupdesc2 ,
505
+ ERR_PART_DESC_CONVERT );
506
+ #endif
507
+
508
+ /* Prepare the map structure */
509
+ tuple_map = (TupleConversionMap * ) palloc (sizeof (TupleConversionMap ));
510
+ tuple_map -> indesc = child_tupdesc1 ;
511
+ tuple_map -> outdesc = child_tupdesc2 ;
512
+ tuple_map -> attrMap = attrMap ;
513
+
514
+ /* preallocate workspace for Datum arrays */
515
+ n = child_tupdesc1 -> natts ;
516
+ tuple_map -> outvalues = (Datum * ) palloc (n * sizeof (Datum ));
517
+ tuple_map -> outisnull = (bool * ) palloc (n * sizeof (bool ));
518
+
519
+ n = child_tupdesc1 -> natts + 1 ; /* +1 for NULL */
520
+ tuple_map -> invalues = (Datum * ) palloc (n * sizeof (Datum ));
521
+ tuple_map -> inisnull = (bool * ) palloc (n * sizeof (bool ));
522
+
523
+ tuple_map -> invalues [0 ] = (Datum ) 0 ; /* set up the NULL entry */
524
+ tuple_map -> inisnull [0 ] = true;
525
+
526
+ return tuple_map ;
527
+ }
528
+
529
+ /* Destroy tuple conversion map */
530
+ void
531
+ destroy_tuple_map (TupleConversionMap * tuple_map )
532
+ {
533
+ if (tuple_map )
534
+ {
535
+ FreeTupleDesc (tuple_map -> indesc );
536
+ FreeTupleDesc (tuple_map -> outdesc );
537
+
538
+ free_conversion_map (tuple_map );
539
+ }
540
+ }
471
541
472
542
/*
473
543
* -----------------------------------
@@ -812,6 +882,7 @@ partition_filter_exec(CustomScanState *node)
812
882
MemoryContext old_mcxt ;
813
883
ResultRelInfoHolder * rri_holder ;
814
884
ResultRelInfo * rri ;
885
+ JunkFilter * junkfilter = NULL ;
815
886
#if PG_VERSION_NUM >= 140000
816
887
PartitionRouterState * pr_state = linitial (node -> custom_ps );
817
888
@@ -827,6 +898,8 @@ partition_filter_exec(CustomScanState *node)
827
898
*/
828
899
reint_partition_filter_state (state , pr_state -> current_rri );
829
900
}
901
+ #else
902
+ junkfilter = estate -> es_result_relation_info -> ri_junkFilter ;
830
903
#endif
831
904
832
905
/* Switch to per-tuple context */
@@ -844,18 +917,58 @@ partition_filter_exec(CustomScanState *node)
844
917
/* Magic: replace parent's ResultRelInfo with ours */
845
918
estate -> es_result_relation_info = rri ;
846
919
920
+ /*
921
+ * Besides 'transform map' we should process two cases:
922
+ * 1) CMD_UPDATE, row moved to other partition, junkfilter == NULL
923
+ * (filled in router_set_slot() for SELECT + INSERT);
924
+ * we should clear attribute 'ctid' (do not insert it into database);
925
+ * 2) CMD_INSERT/CMD_UPDATE operations for partitions with deleted column(s),
926
+ * junkfilter == NULL.
927
+ */
847
928
/* If there's a transform map, rebuild the tuple */
848
- if (rri_holder -> tuple_map )
929
+ if (rri_holder -> tuple_map ||
930
+ (!junkfilter &&
931
+ (state -> command_type == CMD_INSERT || state -> command_type == CMD_UPDATE ) &&
932
+ (slot -> tts_tupleDescriptor -> natts > rri -> ri_RelationDesc -> rd_att -> natts /* extra fields */
933
+ #if PG_VERSION_NUM < 120000
934
+ /*
935
+ * If we have a regular physical tuple 'slot->tts_tuple' and
936
+ * it's locally palloc'd => we will use this tuple in
937
+ * ExecMaterializeSlot() instead of materialize the slot, so
938
+ * need to check number of attributes for this tuple:
939
+ */
940
+ || (slot -> tts_tuple && slot -> tts_shouldFree &&
941
+ HeapTupleHeaderGetNatts (slot -> tts_tuple -> t_data ) >
942
+ rri -> ri_RelationDesc -> rd_att -> natts /* extra fields */ )
943
+ #endif
944
+ )))
849
945
{
850
- Relation child_rel = rri -> ri_RelationDesc ;
851
-
852
- /* xxx why old code decided to materialize it? */
853
946
#if PG_VERSION_NUM < 120000
854
947
HeapTuple htup_old ,
855
948
htup_new ;
949
+ #endif
950
+ Relation child_rel = rri -> ri_RelationDesc ;
951
+ TupleConversionMap * tuple_map ;
856
952
953
+ if (rri_holder -> tuple_map )
954
+ tuple_map = rri_holder -> tuple_map ;
955
+ else
956
+ {
957
+ if (!rri_holder -> tuple_map_child )
958
+ { /*
959
+ * Generate child->child tuple transformation map. We need to
960
+ * convert tuples because child TupleDesc has extra
961
+ * columns ('ctid' etc.) and need remove them.
962
+ */
963
+ rri_holder -> tuple_map_child = build_part_tuple_map_child (child_rel );
964
+ }
965
+ tuple_map = rri_holder -> tuple_map_child ;
966
+ }
967
+
968
+ /* xxx why old code decided to materialize it? */
969
+ #if PG_VERSION_NUM < 120000
857
970
htup_old = ExecMaterializeSlot (slot );
858
- htup_new = do_convert_tuple (htup_old , rri_holder -> tuple_map );
971
+ htup_new = do_convert_tuple (htup_old , tuple_map );
859
972
ExecClearTuple (slot );
860
973
#endif
861
974
@@ -872,7 +985,7 @@ partition_filter_exec(CustomScanState *node)
872
985
/* TODO: why should we *always* set a new slot descriptor? */
873
986
ExecSetSlotDescriptor (state -> tup_convert_slot , RelationGetDescr (child_rel ));
874
987
#if PG_VERSION_NUM >= 120000
875
- slot = execute_attr_map_slot (rri_holder -> tuple_map -> attrMap , slot , state -> tup_convert_slot );
988
+ slot = execute_attr_map_slot (tuple_map -> attrMap , slot , state -> tup_convert_slot );
876
989
#else
877
990
slot = ExecStoreTuple (htup_new , state -> tup_convert_slot , InvalidBuffer , true);
878
991
#endif
0 commit comments