diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 0b0696e61e..b45972682f 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -2316,6 +2316,7 @@ CopyFrom(CopyState cstate) bool *nulls; ResultRelInfo *resultRelInfo; ResultRelInfo *target_resultRelInfo; + ResultRelInfo *prev_part_rel = NULL; EState *estate = CreateExecutorState(); /* for ExecConstraints() */ ModifyTableState *mtstate; ExprContext *econtext; @@ -2331,7 +2332,6 @@ CopyFrom(CopyState cstate) CopyInsertMethod insertMethod; uint64 processed = 0; int nBufferedTuples = 0; - int prev_leaf_part_index = -1; bool has_before_insert_row_trig; bool has_instead_insert_row_trig; bool leafpart_use_multi_insert = false; @@ -2685,19 +2685,24 @@ CopyFrom(CopyState cstate) /* Determine the partition to heap_insert the tuple into */ if (proute) { - int leaf_part_index; - TupleConversionMap *map; + TupleTableSlot *partition_slot = NULL; + TupleConversionMap *child_to_parent_map, + *parent_to_child_map; /* * Attempt to find a partition suitable for this tuple. * ExecFindPartition() will raise an error if none can be found. + * This replaces the original target ResultRelInfo with + * partition's. */ - leaf_part_index = ExecFindPartition(mtstate, target_resultRelInfo, - proute, slot, estate); - Assert(leaf_part_index >= 0 && - leaf_part_index < proute->num_partitions); + resultRelInfo = ExecFindPartition(mtstate, target_resultRelInfo, + proute, slot, estate, + &parent_to_child_map, + &partition_slot, + &child_to_parent_map); + Assert(resultRelInfo != NULL); - if (prev_leaf_part_index != leaf_part_index) + if (prev_part_rel != resultRelInfo) { /* Check if we can multi-insert into this partition */ if (insertMethod == CIM_MULTI_CONDITIONAL) @@ -2710,12 +2715,9 @@ CopyFrom(CopyState cstate) if (nBufferedTuples > 0) { ExprContext *swapcontext; - ResultRelInfo *presultRelInfo; - - presultRelInfo = proute->partitions[prev_leaf_part_index]; CopyFromInsertBatch(cstate, estate, mycid, hi_options, - presultRelInfo, myslot, bistate, + prev_part_rel, myslot, bistate, nBufferedTuples, bufferedTuples, firstBufferedLineNo); nBufferedTuples = 0; @@ -2772,13 +2774,6 @@ CopyFrom(CopyState cstate) } } - /* - * Overwrite resultRelInfo with the corresponding partition's - * one. - */ - resultRelInfo = proute->partitions[leaf_part_index]; - Assert(resultRelInfo != NULL); - /* Determine which triggers exist on this partition */ has_before_insert_row_trig = (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->trig_insert_before_row); @@ -2804,7 +2799,7 @@ CopyFrom(CopyState cstate) * buffer when the partition being inserted into changes. */ ReleaseBulkInsertStatePin(bistate); - prev_leaf_part_index = leaf_part_index; + prev_part_rel = resultRelInfo; } /* @@ -2826,8 +2821,7 @@ CopyFrom(CopyState cstate) * tuplestore format. */ cstate->transition_capture->tcs_original_insert_tuple = NULL; - cstate->transition_capture->tcs_map = - PartitionChildToParentMap(proute, leaf_part_index); + cstate->transition_capture->tcs_map = child_to_parent_map; } else { @@ -2844,16 +2838,13 @@ CopyFrom(CopyState cstate) * We might need to convert from the parent rowtype to the * partition rowtype. */ - map = PartitionParentToChildMap(proute, leaf_part_index); - if (map != NULL) + if (parent_to_child_map != NULL) { - TupleTableSlot *new_slot; MemoryContext oldcontext; - Assert(proute->partition_tuple_slots != NULL && - proute->partition_tuple_slots[leaf_part_index] != NULL); - new_slot = proute->partition_tuple_slots[leaf_part_index]; - slot = execute_attr_map_slot(map->attrMap, slot, new_slot); + Assert(partition_slot != NULL); + slot = execute_attr_map_slot(parent_to_child_map->attrMap, + slot, partition_slot); /* * Get the tuple in the per-tuple context, so that it will be @@ -2997,12 +2988,8 @@ CopyFrom(CopyState cstate) { if (insertMethod == CIM_MULTI_CONDITIONAL) { - ResultRelInfo *presultRelInfo; - - presultRelInfo = proute->partitions[prev_leaf_part_index]; - CopyFromInsertBatch(cstate, estate, mycid, hi_options, - presultRelInfo, myslot, bistate, + prev_part_rel, myslot, bistate, nBufferedTuples, bufferedTuples, firstBufferedLineNo); } diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 542578102f..4b27874f01 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -70,11 +70,16 @@ typedef struct PartitionDispatchData static void ExecHashSubPlanResultRelsByOid(ModifyTableState *mtstate, PartitionTupleRouting *proute); static void ExecExpandRoutingArrays(PartitionTupleRouting *proute); -static int ExecInitPartitionInfo(ModifyTableState *mtstate, +static ResultRelInfo *ExecInitPartitionInfo(ModifyTableState *mtstate, ResultRelInfo *rootResultRelInfo, PartitionTupleRouting *proute, EState *estate, PartitionDispatch dispatch, int partidx); +static void ExecInitRoutingInfo(ModifyTableState *mtstate, + EState *estate, + PartitionTupleRouting *proute, + ResultRelInfo *partRelInfo, + int partidx); static PartitionDispatch ExecInitPartitionDispatchInfo(PartitionTupleRouting *proute, Oid partoid, PartitionDispatch parent_pd, int partidx); static void FormPartitionKeyDatum(PartitionDispatch pd, @@ -194,14 +199,25 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, Relation rel) * partition key(s) * * If no leaf partition is found, this routine errors out with the appropriate - * error message, else it returns the index of the leaf partition's - * ResultRelInfo in the proute->partitions array. + * error message, else it returns the leaf partition's ResultRelInfo. + * + * *parent_to_child_map is set if the parent tuples would need to be converted + * before inserting into the chosen partition. In that case, + * *partition_tuple_slot is also set. + * + * *child_to_parent_map is set if the tuples inserted into the partition after + * routing would need to be converted back to parent's rowtype for storing + * into the transition tuple store, that is, only if transition capture is + * active for the command. */ -int +ResultRelInfo * ExecFindPartition(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, PartitionTupleRouting *proute, - TupleTableSlot *slot, EState *estate) + TupleTableSlot *slot, EState *estate, + TupleConversionMap **parent_to_child_map, + TupleTableSlot **partition_slot, + TupleConversionMap **child_to_parent_map) { PartitionDispatch *pd = proute->partition_dispatch_info; Datum values[PARTITION_MAX_KEYS]; @@ -214,6 +230,9 @@ ExecFindPartition(ModifyTableState *mtstate, TupleTableSlot *myslot = NULL; MemoryContext oldcxt; + *parent_to_child_map = *child_to_parent_map = NULL; + *partition_slot = NULL; + /* use per-tuple context here to avoid leaking memory */ oldcxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); @@ -274,7 +293,8 @@ ExecFindPartition(ModifyTableState *mtstate, if (partdesc->is_leaf[partidx]) { - int result = -1; + int index = -1; + ResultRelInfo *result = NULL; /* * Get this leaf partition's index in the @@ -285,7 +305,8 @@ ExecFindPartition(ModifyTableState *mtstate, { /* ResultRelInfo already built */ Assert(dispatch->indexes[partidx] < proute->num_partitions); - result = dispatch->indexes[partidx]; + index = dispatch->indexes[partidx]; + result = proute->partitions[index]; } else { @@ -296,36 +317,58 @@ ExecFindPartition(ModifyTableState *mtstate, */ if (proute->subplan_resultrel_hash) { - ResultRelInfo *rri; Oid partoid = partdesc->oids[partidx]; - rri = hash_search(proute->subplan_resultrel_hash, - &partoid, HASH_FIND, NULL); + result = hash_search(proute->subplan_resultrel_hash, + &partoid, HASH_FIND, NULL); - if (rri) + if (result) { - result = proute->num_partitions++; - dispatch->indexes[partidx] = result; + index = proute->num_partitions++; + dispatch->indexes[partidx] = index; /* Allocate more space in the arrays, if required */ - if (result >= proute->partitions_allocsize) + if (index >= proute->partitions_allocsize) ExecExpandRoutingArrays(proute); /* Save here for later use. */ - proute->partitions[result] = rri; + proute->partitions[index] = result; + + /* + * We need to make this result rel "routable" if it's + * the first time is is being used for routing. Also, + * we would've only checked if the relation is a valid + * target for UPDATE when creating this ResultRelInfo + * and now we're about to insert the routed tuple into + * it, so we need to check if it's a valid target for + * INSERT as well. + */ + if (!result->ri_PartitionReadyForRouting) + { + CheckValidResultRel(result, CMD_INSERT); + + /* + * Also set up information needed for routing + * tuples to the partition. + */ + ExecInitRoutingInfo(mtstate, estate, proute, + result, index); + } } } /* We need to create a new one. */ - if (result < 0) + if (result == NULL) { MemoryContextSwitchTo(oldcxt); result = ExecInitPartitionInfo(mtstate, resultRelInfo, proute, estate, dispatch, partidx); MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); - Assert(result >= 0 && result < proute->num_partitions); + Assert(result != NULL); + index = dispatch->indexes[partidx]; + Assert(index >= 0 && index < proute->num_partitions); } } @@ -335,6 +378,26 @@ ExecFindPartition(ModifyTableState *mtstate, MemoryContextSwitchTo(oldcxt); ecxt->ecxt_scantuple = ecxt_scantuple_old; + + /* Set the values of result maps and the slot if needed. */ + if (proute->parent_child_tupconv_maps) + { + *parent_to_child_map = proute->parent_child_tupconv_maps[index]; + + /* + * If non-NULL, correponding tuple slot must have been + * initialized for the partition. + */ + if (*parent_to_child_map != NULL) + { + *partition_slot = proute->partition_tuple_slots[index]; + Assert(*partition_slot != NULL); + } + } + + if (proute->child_parent_tupconv_maps) + *child_to_parent_map = proute->child_parent_tupconv_maps[index]; + return result; } else @@ -374,6 +437,9 @@ ExecFindPartition(ModifyTableState *mtstate, } } } + + Assert(false); + return NULL; /* keep compiler quiet */ } /* @@ -475,9 +541,10 @@ ExecExpandRoutingArrays(PartitionTupleRouting *proute) * ExecInitPartitionInfo * Initialize ResultRelInfo and other information for a partition * and store it in the next empty slot in proute's partitions array. - * Return the index of the array element. + * + * Returns the ResultRelInfo */ -static int +static ResultRelInfo * ExecInitPartitionInfo(ModifyTableState *mtstate, ResultRelInfo *rootResultRelInfo, PartitionTupleRouting *proute, @@ -742,9 +809,10 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, */ if (node->onConflictAction == ONCONFLICT_UPDATE) { - TupleConversionMap *map; + TupleConversionMap *map = NULL; - map = PartitionParentToChildMap(proute, part_result_rel_index); + if (proute->parent_child_tupconv_maps) + map = proute->parent_child_tupconv_maps[part_result_rel_index]; Assert(node->onConflictSet != NIL); Assert(rootResultRelInfo->ri_onConflict != NULL); @@ -849,14 +917,14 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, MemoryContextSwitchTo(oldContext); - return part_result_rel_index; + return leaf_part_rri; } /* * ExecInitRoutingInfo * Set up information needed for routing tuples to a leaf partition */ -void +static void ExecInitRoutingInfo(ModifyTableState *mtstate, EState *estate, PartitionTupleRouting *proute, diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 840b98811f..18c55e2b19 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1697,46 +1697,22 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate, TupleTableSlot *slot) { ModifyTable *node; - int partidx; ResultRelInfo *partrel; HeapTuple tuple; - TupleConversionMap *map; + TupleTableSlot *partition_slot = NULL; + TupleConversionMap *child_to_parent_map, + *parent_to_child_map; /* * Determine the target partition. If ExecFindPartition does not find a - * partition after all, it doesn't return here; otherwise, the returned - * value is to be used as an index into the arrays for the ResultRelInfo - * and TupleConversionMap for the partition. + * partition after all, it doesn't return here. */ - partidx = ExecFindPartition(mtstate, targetRelInfo, proute, slot, estate); - Assert(partidx >= 0 && partidx < proute->num_partitions); - - Assert(proute->partitions[partidx] != NULL); - /* Get the ResultRelInfo corresponding to the selected partition. */ - partrel = proute->partitions[partidx]; + partrel = ExecFindPartition(mtstate, targetRelInfo, proute, slot, estate, + &parent_to_child_map, &partition_slot, + &child_to_parent_map); Assert(partrel != NULL); /* - * Check whether the partition is routable if we didn't yet - * - * Note: an UPDATE of a partition key invokes an INSERT that moves the - * tuple to a new partition. This check would be applied to a subplan - * partition of such an UPDATE that is chosen as the partition to route - * the tuple to. The reason we do this check here rather than in - * ExecSetupPartitionTupleRouting is to avoid aborting such an UPDATE - * unnecessarily due to non-routable subplan partitions that may not be - * chosen for update tuple movement after all. - */ - if (!partrel->ri_PartitionReadyForRouting) - { - /* Verify the partition is a valid target for INSERT. */ - CheckValidResultRel(partrel, CMD_INSERT); - - /* Set up information needed for routing tuples to the partition. */ - ExecInitRoutingInfo(mtstate, estate, proute, partrel, partidx); - } - - /* * Make it look like we are inserting into the partition. */ estate->es_result_relation_info = partrel; @@ -1758,8 +1734,7 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate, * to be ready to convert their result back to tuplestore format. */ mtstate->mt_transition_capture->tcs_original_insert_tuple = NULL; - mtstate->mt_transition_capture->tcs_map = - PartitionChildToParentMap(proute, partidx); + mtstate->mt_transition_capture->tcs_map = child_to_parent_map; } else { @@ -1772,23 +1747,17 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate, } } if (mtstate->mt_oc_transition_capture != NULL) - { - mtstate->mt_oc_transition_capture->tcs_map = - PartitionChildToParentMap(proute, partidx); - } + mtstate->mt_oc_transition_capture->tcs_map = child_to_parent_map; /* * Convert the tuple, if necessary. */ - map = PartitionParentToChildMap(proute, partidx); - if (map != NULL) + if (parent_to_child_map != NULL) { - TupleTableSlot *new_slot; - Assert(proute->partition_tuple_slots != NULL && - proute->partition_tuple_slots[partidx] != NULL); - new_slot = proute->partition_tuple_slots[partidx]; - slot = execute_attr_map_slot(map->attrMap, slot, new_slot); + Assert(partition_slot != NULL); + slot = execute_attr_map_slot(parent_to_child_map->attrMap, slot, + partition_slot); } /* Initialize information needed to handle ON CONFLICT DO UPDATE. */ diff --git a/src/include/executor/execPartition.h b/src/include/executor/execPartition.h index 45d5f6a8d0..7c8314362c 100644 --- a/src/include/executor/execPartition.h +++ b/src/include/executor/execPartition.h @@ -122,20 +122,6 @@ typedef struct PartitionTupleRouting } PartitionTupleRouting; /* - * Accessor macros for tuple conversion maps contained in - * PartitionTupleRouting. Beware of multiple evaluations of p! - */ -#define PartitionChildToParentMap(p, i) \ - ((p)->child_parent_tupconv_maps != NULL ? \ - (p)->child_parent_tupconv_maps[(i)] : \ - NULL) - -#define PartitionParentToChildMap(p, i) \ - ((p)->parent_child_tupconv_maps != NULL ? \ - (p)->parent_child_tupconv_maps[(i)] : \ - NULL) - -/* * PartitionedRelPruningData - Per-partitioned-table data for run-time pruning * of partitions. For a multilevel partitioned table, we have one of these * for the topmost partition plus one for each non-leaf child partition. @@ -223,20 +209,14 @@ typedef struct PartitionPruneState extern PartitionTupleRouting *ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, Relation rel); -extern int ExecFindPartition(ModifyTableState *mtstate, +extern ResultRelInfo *ExecFindPartition(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, PartitionTupleRouting *proute, TupleTableSlot *slot, - EState *estate); -extern ResultRelInfo *ExecGetPartitionInfo(ModifyTableState *mtstate, - ResultRelInfo *resultRelInfo, - PartitionTupleRouting *proute, - EState *estate, int partidx); -extern void ExecInitRoutingInfo(ModifyTableState *mtstate, - EState *estate, - PartitionTupleRouting *proute, - ResultRelInfo *partRelInfo, - int partidx); + EState *estate, + TupleConversionMap **parent_to_child_map, + TupleTableSlot **partition_slot, + TupleConversionMap **child_to_parent_map); extern void ExecCleanupTupleRouting(ModifyTableState *mtstate, PartitionTupleRouting *proute); extern PartitionPruneState *ExecCreatePartitionPruneState(PlanState *planstate,