Commit c5b7ba4e authored by Tom Lane's avatar Tom Lane

Postpone some stuff out of ExecInitModifyTable.

Arrange to do some things on-demand, rather than immediately during
executor startup, because there's a fair chance of never having to do
them at all:

* Don't open result relations' indexes until needed.

* Don't initialize partition tuple routing, nor the child-to-root
tuple conversion map, until needed.

This wins in UPDATEs on partitioned tables when only some of the
partitions will actually receive updates; with larger partition
counts the savings is quite noticeable.  Also, we can remove some
sketchy heuristics in ExecInitModifyTable about whether to set up
tuple routing.

Also, remove execPartition.c's private hash table tracking which
partitions were already opened by the ModifyTable node.  Instead
use the hash added to ModifyTable itself by commit 86dc9005.

To allow lazy computation of the conversion maps, we now set
ri_RootResultRelInfo in all child ResultRelInfos.  We formerly set it
only in some, not terribly well-defined, cases.  This has user-visible
side effects in that now more error messages refer to the root
relation instead of some partition (and provide error data in the
root's column order, too).  It looks to me like this is a strict
improvement in consistency, so I don't have a problem with the
output changes visible in this commit.

Extracted from a larger patch, which seemed to me to be too messy
to push in one commit.

Amit Langote, reviewed at different times by Heikki Linnakangas and
myself

Discussion: https://postgr.es/m/CA+HiwqG7ZruBmmih3wPsBZ4s0H2EhywrnXEduckY5Hr3fWzPWA@mail.gmail.com
parent a3740c48
......@@ -697,7 +697,7 @@ CopyFrom(CopyFromState cstate)
* CopyFrom tuple routing.
*/
if (cstate->rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
proute = ExecSetupPartitionTupleRouting(estate, NULL, cstate->rel);
proute = ExecSetupPartitionTupleRouting(estate, cstate->rel);
if (cstate->whereClause)
cstate->qualexpr = ExecInitQual(castNode(List, cstate->whereClause),
......
......@@ -5479,7 +5479,7 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
if (row_trigger && transition_capture != NULL)
{
TupleTableSlot *original_insert_tuple = transition_capture->tcs_original_insert_tuple;
TupleConversionMap *map = relinfo->ri_ChildToRootMap;
TupleConversionMap *map = ExecGetChildToRootMap(relinfo);
bool delete_old_table = transition_capture->tcs_delete_old_table;
bool update_old_table = transition_capture->tcs_update_old_table;
bool update_new_table = transition_capture->tcs_update_new_table;
......
......@@ -1231,11 +1231,19 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
resultRelInfo->ri_ReturningSlot = NULL;
resultRelInfo->ri_TrigOldSlot = NULL;
resultRelInfo->ri_TrigNewSlot = NULL;
/*
* Only ExecInitPartitionInfo() and ExecInitPartitionDispatchInfo() pass
* non-NULL partition_root_rri. For child relations that are part of the
* initial query rather than being dynamically added by tuple routing,
* this field is filled in ExecInitModifyTable().
*/
resultRelInfo->ri_RootResultRelInfo = partition_root_rri;
resultRelInfo->ri_RootToPartitionMap = NULL; /* set by
* ExecInitRoutingInfo */
resultRelInfo->ri_PartitionTupleSlot = NULL; /* ditto */
resultRelInfo->ri_ChildToRootMap = NULL;
resultRelInfo->ri_ChildToRootMapValid = false;
resultRelInfo->ri_CopyMultiInsertBuffer = NULL;
}
......
This diff is collapsed.
......@@ -1225,6 +1225,32 @@ ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo)
return relInfo->ri_ReturningSlot;
}
/*
* Return the map needed to convert given child result relation's tuples to
* the rowtype of the query's main target ("root") relation. Note that a
* NULL result is valid and means that no conversion is needed.
*/
TupleConversionMap *
ExecGetChildToRootMap(ResultRelInfo *resultRelInfo)
{
/* If we didn't already do so, compute the map for this child. */
if (!resultRelInfo->ri_ChildToRootMapValid)
{
ResultRelInfo *rootRelInfo = resultRelInfo->ri_RootResultRelInfo;
if (rootRelInfo)
resultRelInfo->ri_ChildToRootMap =
convert_tuples_by_name(RelationGetDescr(resultRelInfo->ri_RelationDesc),
RelationGetDescr(rootRelInfo->ri_RelationDesc));
else /* this isn't a child result rel */
resultRelInfo->ri_ChildToRootMap = NULL;
resultRelInfo->ri_ChildToRootMapValid = true;
}
return resultRelInfo->ri_ChildToRootMap;
}
/* Return a bitmap representing columns being inserted */
Bitmapset *
ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate)
......
This diff is collapsed.
......@@ -1583,7 +1583,7 @@ apply_handle_tuple_routing(ResultRelInfo *relinfo,
mtstate->ps.state = estate;
mtstate->operation = operation;
mtstate->resultRelInfo = relinfo;
proute = ExecSetupPartitionTupleRouting(estate, mtstate, parentrel);
proute = ExecSetupPartitionTupleRouting(estate, parentrel);
/*
* Find the partition to which the "search tuple" belongs.
......
......@@ -111,7 +111,6 @@ typedef struct PartitionPruneState
} PartitionPruneState;
extern PartitionTupleRouting *ExecSetupPartitionTupleRouting(EState *estate,
ModifyTableState *mtstate,
Relation rel);
extern ResultRelInfo *ExecFindPartition(ModifyTableState *mtstate,
ResultRelInfo *rootResultRelInfo,
......
......@@ -596,6 +596,7 @@ extern int ExecCleanTargetListLength(List *targetlist);
extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relInfo);
extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo);
extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo);
extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo);
extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate);
extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate);
......@@ -645,9 +646,15 @@ extern void CheckCmdReplicaIdentity(Relation rel, CmdType cmd);
extern void CheckSubscriptionRelkind(char relkind, const char *nspname,
const char *relname);
/* needed by trigger.c */
/*
* prototypes from functions in nodeModifyTable.c
*/
extern TupleTableSlot *ExecGetUpdateNewTuple(ResultRelInfo *relinfo,
TupleTableSlot *planSlot,
TupleTableSlot *oldSlot);
extern ResultRelInfo *ExecLookupResultRelByOid(ModifyTableState *node,
Oid resultoid,
bool missing_ok,
bool update_cache);
#endif /* EXECUTOR_H */
......@@ -512,10 +512,12 @@ typedef struct ResultRelInfo
/*
* 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.
* actually mentioned in the query (called "root"). Computed only if
* needed. A NULL map value indicates that no conversion is needed, so we
* must have a separate flag to show if the map has been computed.
*/
TupleConversionMap *ri_ChildToRootMap;
bool ri_ChildToRootMapValid;
/* for use by copyfrom.c when performing multi-inserts */
struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer;
......
......@@ -2492,7 +2492,7 @@ ERROR: new row for relation "errtst_child_plaindef" violates check constraint "
DETAIL: Failing row contains (10, 1, 15).
UPDATE errtst_parent SET data = data + 10 WHERE partid = 20;
ERROR: new row for relation "errtst_child_reorder" violates check constraint "errtst_child_reorder_data_check"
DETAIL: Failing row contains (15, 1, 20).
DETAIL: Failing row contains (20, 1, 15).
-- direct leaf partition update, without partition id violation
BEGIN;
UPDATE errtst_child_fastdef SET partid = 1 WHERE partid = 0;
......
......@@ -720,7 +720,7 @@ DETAIL: Failing row contains (a, b, c) = (aaa, null, null).
-- simple update.
UPDATE errtst SET b = NULL;
ERROR: null value in column "b" of relation "errtst_part_1" violates not-null constraint
DETAIL: Failing row contains (b) = (null).
DETAIL: Failing row contains (a, b, c) = (aaa, null, ccc).
-- partitioning key is updated, doesn't move the row.
UPDATE errtst SET a = 'aaa', b = NULL;
ERROR: null value in column "b" of relation "errtst_part_1" violates not-null constraint
......
......@@ -342,8 +342,8 @@ DETAIL: Failing row contains (105, 85, null, b, 15).
-- fail, no partition key update, so no attempt to move tuple,
-- but "a = 'a'" violates partition constraint enforced by root partition)
UPDATE part_b_10_b_20 set a = 'a';
ERROR: new row for relation "part_c_1_100" violates partition constraint
DETAIL: Failing row contains (null, 1, 96, 12, a).
ERROR: new row for relation "part_b_10_b_20" violates partition constraint
DETAIL: Failing row contains (null, 96, a, 12, 1).
-- ok, partition key update, no constraint violation
UPDATE range_parted set d = d - 10 WHERE d > 10;
-- ok, no partition key update, no constraint violation
......@@ -373,8 +373,8 @@ UPDATE part_b_10_b_20 set c = c + 20 returning c, b, a;
-- fail, row movement happens only within the partition subtree.
UPDATE part_b_10_b_20 set b = b - 6 WHERE c > 116 returning *;
ERROR: new row for relation "part_d_1_15" violates partition constraint
DETAIL: Failing row contains (2, 117, 2, b, 7).
ERROR: new row for relation "part_b_10_b_20" violates partition constraint
DETAIL: Failing row contains (2, 117, b, 7, 2).
-- ok, row movement, with subset of rows moved into different partition.
UPDATE range_parted set b = b - 6 WHERE c > 116 returning a, b + c;
a | ?column?
......@@ -815,8 +815,8 @@ INSERT into sub_parted VALUES (1,2,10);
-- Test partition constraint violation when intermediate ancestor is used and
-- constraint is inherited from upper root.
UPDATE sub_parted set a = 2 WHERE c = 10;
ERROR: new row for relation "sub_part2" violates partition constraint
DETAIL: Failing row contains (2, 10, 2).
ERROR: new row for relation "sub_parted" violates partition constraint
DETAIL: Failing row contains (2, 2, 10).
-- Test update-partition-key, where the unpruned partitions do not have their
-- partition keys updated.
SELECT tableoid::regclass::text, * FROM list_parted WHERE a = 2 ORDER BY 1;
......
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