Commit 69735336 authored by Heikki Linnakangas's avatar Heikki Linnakangas

Revise child-to-root tuple conversion map management.

Store the tuple conversion map to convert a tuple from a child table's
format to the root format in a new ri_ChildToRootMap field in
ResultRelInfo. It is initialized if transition tuple capture for FOR
STATEMENT triggers or INSERT tuple routing on a partitioned table is
needed. Previously, ModifyTable kept the maps in the per-subplan
ModifyTableState->mt_per_subplan_tupconv_maps array, or when tuple
routing was used, in
ResultRelInfo->ri_Partitioninfo->pi_PartitionToRootMap. The new field
replaces both of those.

Now that the child-to-root tuple conversion map is always available in
ResultRelInfo (when needed), remove the TransitionCaptureState.tcs_map
field. The callers of Exec*Trigger() functions no longer need to set or
save it, which is much less confusing and bug-prone. Also, as a future
optimization, this will allow us to delay creating the map for a given
result relation until the relation is actually processed during
execution.

Author: Amit Langote
Discussion: https://www.postgresql.org/message-id/CA%2BHiwqHtCWLdK-LO%3DNEsvOdHx%2B7yv4mE_zYK0i3BH7dXb-wxog%40mail.gmail.com
parent f49b85d7
...@@ -3106,31 +3106,14 @@ CopyFrom(CopyState cstate) ...@@ -3106,31 +3106,14 @@ CopyFrom(CopyState cstate)
/* /*
* If we're capturing transition tuples, we might need to convert * If we're capturing transition tuples, we might need to convert
* from the partition rowtype to root rowtype. * from the partition rowtype to root rowtype. But if there are no
* BEFORE triggers on the partition that could change the tuple,
* we can just remember the original unconverted tuple to avoid a
* needless round trip conversion.
*/ */
if (cstate->transition_capture != NULL) if (cstate->transition_capture != NULL)
{ cstate->transition_capture->tcs_original_insert_tuple =
if (has_before_insert_row_trig) !has_before_insert_row_trig ? myslot : NULL;
{
/*
* If there are any BEFORE triggers on the partition,
* we'll have to be ready to convert their result back to
* tuplestore format.
*/
cstate->transition_capture->tcs_original_insert_tuple = NULL;
cstate->transition_capture->tcs_map =
resultRelInfo->ri_PartitionInfo->pi_PartitionToRootMap;
}
else
{
/*
* Otherwise, just remember the original unconverted
* tuple, to avoid a needless round trip conversion.
*/
cstate->transition_capture->tcs_original_insert_tuple = myslot;
cstate->transition_capture->tcs_map = NULL;
}
}
/* /*
* We might need to convert from the root rowtype to the partition * We might need to convert from the root rowtype to the partition
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include "commands/defrem.h" #include "commands/defrem.h"
#include "commands/trigger.h" #include "commands/trigger.h"
#include "executor/executor.h" #include "executor/executor.h"
#include "executor/execPartition.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "nodes/bitmapset.h" #include "nodes/bitmapset.h"
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
...@@ -4292,9 +4293,10 @@ GetAfterTriggersTableData(Oid relid, CmdType cmdType) ...@@ -4292,9 +4293,10 @@ GetAfterTriggersTableData(Oid relid, CmdType cmdType)
* If there are no triggers in 'trigdesc' that request relevant transition * If there are no triggers in 'trigdesc' that request relevant transition
* tables, then return NULL. * tables, then return NULL.
* *
* The resulting object can be passed to the ExecAR* functions. The caller * The resulting object can be passed to the ExecAR* functions. When
* should set tcs_map or tcs_original_insert_tuple as appropriate when dealing * dealing with child tables, the caller can set tcs_original_insert_tuple
* with child tables. * to avoid having to reconstruct the original tuple in the root table's
* format.
* *
* Note that we copy the flags from a parent table into this struct (rather * Note that we copy the flags from a parent table into this struct (rather
* than subsequently using the relation's TriggerDesc directly) so that we can * than subsequently using the relation's TriggerDesc directly) so that we can
...@@ -5389,7 +5391,7 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, ...@@ -5389,7 +5391,7 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
if (row_trigger && transition_capture != NULL) if (row_trigger && transition_capture != NULL)
{ {
TupleTableSlot *original_insert_tuple = transition_capture->tcs_original_insert_tuple; TupleTableSlot *original_insert_tuple = transition_capture->tcs_original_insert_tuple;
TupleConversionMap *map = transition_capture->tcs_map; TupleConversionMap *map = relinfo->ri_ChildToRootMap;
bool delete_old_table = transition_capture->tcs_delete_old_table; bool delete_old_table = transition_capture->tcs_delete_old_table;
bool update_old_table = transition_capture->tcs_update_old_table; bool update_old_table = transition_capture->tcs_update_old_table;
bool update_new_table = transition_capture->tcs_update_new_table; bool update_new_table = transition_capture->tcs_update_new_table;
......
...@@ -1244,6 +1244,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, ...@@ -1244,6 +1244,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
resultRelInfo->ri_TrigNewSlot = NULL; resultRelInfo->ri_TrigNewSlot = NULL;
resultRelInfo->ri_PartitionRoot = partition_root; resultRelInfo->ri_PartitionRoot = partition_root;
resultRelInfo->ri_PartitionInfo = NULL; /* may be set later */ resultRelInfo->ri_PartitionInfo = NULL; /* may be set later */
resultRelInfo->ri_ChildToRootMap = NULL;
resultRelInfo->ri_CopyMultiInsertBuffer = NULL; resultRelInfo->ri_CopyMultiInsertBuffer = NULL;
} }
......
...@@ -907,6 +907,15 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, ...@@ -907,6 +907,15 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
} }
} }
/*
* Also, if transition capture is required, store a map to convert tuples
* from partition's rowtype to the root partition table's.
*/
if (mtstate->mt_transition_capture || mtstate->mt_oc_transition_capture)
leaf_part_rri->ri_ChildToRootMap =
convert_tuples_by_name(RelationGetDescr(leaf_part_rri->ri_RelationDesc),
RelationGetDescr(leaf_part_rri->ri_PartitionRoot));
/* /*
* Since we've just initialized this ResultRelInfo, it's not in any list * Since we've just initialized this ResultRelInfo, it's not in any list
* attached to the estate as yet. Add it, so that it can be found later. * attached to the estate as yet. Add it, so that it can be found later.
...@@ -976,20 +985,6 @@ ExecInitRoutingInfo(ModifyTableState *mtstate, ...@@ -976,20 +985,6 @@ ExecInitRoutingInfo(ModifyTableState *mtstate,
else else
partrouteinfo->pi_PartitionTupleSlot = NULL; partrouteinfo->pi_PartitionTupleSlot = NULL;
/*
* Also, if transition capture is required, store a map to convert tuples
* from partition's rowtype to the root partition table's.
*/
if (mtstate &&
(mtstate->mt_transition_capture || mtstate->mt_oc_transition_capture))
{
partrouteinfo->pi_PartitionToRootMap =
convert_tuples_by_name(RelationGetDescr(partRelInfo->ri_RelationDesc),
RelationGetDescr(partRelInfo->ri_PartitionRoot));
}
else
partrouteinfo->pi_PartitionToRootMap = NULL;
/* /*
* If the partition is a foreign table, let the FDW init itself for * If the partition is a foreign table, let the FDW init itself for
* routing tuples to the partition. * routing tuples to the partition.
......
This diff is collapsed.
...@@ -46,7 +46,7 @@ typedef struct TriggerData ...@@ -46,7 +46,7 @@ typedef struct TriggerData
* The state for capturing old and new tuples into transition tables for a * The state for capturing old and new tuples into transition tables for a
* single ModifyTable node (or other operation source, e.g. copy.c). * single ModifyTable node (or other operation source, e.g. copy.c).
* *
* This is per-caller to avoid conflicts in setting tcs_map or * This is per-caller to avoid conflicts in setting
* tcs_original_insert_tuple. Note, however, that the pointed-to * tcs_original_insert_tuple. Note, however, that the pointed-to
* private data may be shared across multiple callers. * private data may be shared across multiple callers.
*/ */
...@@ -65,14 +65,6 @@ typedef struct TransitionCaptureState ...@@ -65,14 +65,6 @@ typedef struct TransitionCaptureState
bool tcs_update_new_table; bool tcs_update_new_table;
bool tcs_insert_new_table; bool tcs_insert_new_table;
/*
* For UPDATE and DELETE, AfterTriggerSaveEvent may need to convert the
* new and old tuples from a child table's format to the format of the
* relation named in a query so that it is compatible with the transition
* tuplestores. The caller must store the conversion map here if so.
*/
TupleConversionMap *tcs_map;
/* /*
* For INSERT and COPY, it would be wasteful to convert tuples from child * For INSERT and COPY, it would be wasteful to convert tuples from child
* format to parent format after they have already been converted in the * format to parent format after they have already been converted in the
......
...@@ -36,12 +36,6 @@ typedef struct PartitionRoutingInfo ...@@ -36,12 +36,6 @@ typedef struct PartitionRoutingInfo
*/ */
TupleConversionMap *pi_RootToPartitionMap; TupleConversionMap *pi_RootToPartitionMap;
/*
* Map for converting tuples in partition format into the root partitioned
* table format, or NULL if no conversion is required.
*/
TupleConversionMap *pi_PartitionToRootMap;
/* /*
* Slot to store tuples in partition format, or NULL when no translation * Slot to store tuples in partition format, or NULL when no translation
* is required between root and partition. * is required between root and partition.
......
...@@ -486,6 +486,13 @@ typedef struct ResultRelInfo ...@@ -486,6 +486,13 @@ typedef struct ResultRelInfo
/* info for partition tuple routing (NULL if not set up yet) */ /* info for partition tuple routing (NULL if not set up yet) */
struct PartitionRoutingInfo *ri_PartitionInfo; struct PartitionRoutingInfo *ri_PartitionInfo;
/*
* Map to convert child result relation tuples to the format of the table
* actually mentioned in the query (called "root"). Set only if
* transition tuple capture or update partition row movement is active.
*/
TupleConversionMap *ri_ChildToRootMap;
/* for use by copy.c when performing multi-inserts */ /* for use by copy.c when performing multi-inserts */
struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer; struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer;
} ResultRelInfo; } ResultRelInfo;
...@@ -1179,9 +1186,6 @@ typedef struct ModifyTableState ...@@ -1179,9 +1186,6 @@ typedef struct ModifyTableState
/* controls transition table population for INSERT...ON CONFLICT UPDATE */ /* controls transition table population for INSERT...ON CONFLICT UPDATE */
struct TransitionCaptureState *mt_oc_transition_capture; struct TransitionCaptureState *mt_oc_transition_capture;
/* Per plan map for tuple conversion from child to root */
TupleConversionMap **mt_per_subplan_tupconv_maps;
} ModifyTableState; } ModifyTableState;
/* ---------------- /* ----------------
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment