@@ -239,13 +239,9 @@ fini_result_parts_storage(ResultPartsStorage *parts_storage)
239239}
240240
241241/* 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 );
246243
247- free_conversion_map (rri_holder -> tuple_map );
248- }
244+ destroy_tuple_map (rri_holder -> tuple_map_child );
249245
250246/* Don't forget to close 'prel'! */
251247if (rri_holder -> prel )
@@ -381,6 +377,13 @@ scan_result_parts_storage(ResultPartsStorage *parts_storage, Oid partid)
381377 */
382378rri_holder -> tuple_map = build_part_tuple_map (base_rel ,child_rel );
383379
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+
384387/* Default values */
385388rri_holder -> prel = NULL ;
386389rri_holder -> prel_expr_state = NULL ;
@@ -468,6 +471,73 @@ build_part_tuple_map(Relation base_rel, Relation child_rel)
468471return tuple_map ;
469472}
470473
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+ }
471541
472542/*
473543 * -----------------------------------
@@ -812,6 +882,7 @@ partition_filter_exec(CustomScanState *node)
812882MemoryContext old_mcxt ;
813883ResultRelInfoHolder * rri_holder ;
814884ResultRelInfo * rri ;
885+ JunkFilter * junkfilter = NULL ;
815886#if PG_VERSION_NUM >=140000
816887PartitionRouterState * pr_state = linitial (node -> custom_ps );
817888
@@ -827,6 +898,8 @@ partition_filter_exec(CustomScanState *node)
827898 */
828899reint_partition_filter_state (state ,pr_state -> current_rri );
829900}
901+ #else
902+ junkfilter = estate -> es_result_relation_info -> ri_junkFilter ;
830903#endif
831904
832905/* Switch to per-tuple context */
@@ -844,18 +917,58 @@ partition_filter_exec(CustomScanState *node)
844917/* Magic: replace parent's ResultRelInfo with ours */
845918estate -> es_result_relation_info = rri ;
846919
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+ */
847928/* 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+ )))
849945{
850- Relation child_rel = rri -> ri_RelationDesc ;
851-
852- /* xxx why old code decided to materialize it? */
853946#if PG_VERSION_NUM < 120000
854947HeapTuple htup_old ,
855948htup_new ;
949+ #endif
950+ Relation child_rel = rri -> ri_RelationDesc ;
951+ TupleConversionMap * tuple_map ;
856952
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
857970htup_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 );
859972ExecClearTuple (slot );
860973#endif
861974
@@ -872,7 +985,7 @@ partition_filter_exec(CustomScanState *node)
872985/* TODO: why should we *always* set a new slot descriptor? */
873986ExecSetSlotDescriptor (state -> tup_convert_slot ,RelationGetDescr (child_rel ));
874987#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 );
876989#else
877990slot = ExecStoreTuple (htup_new ,state -> tup_convert_slot ,InvalidBuffer , true);
878991#endif