diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index 7da2058..534ed15 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -2072,6 +2072,77 @@ error_exit: } /* + * For each column of rel which is in the partition key or which appears + * in an expression which is in the partition key, translate the attribute + * number of that column according to the given parent, and add the resulting + * column number to the bitmapset, offset as we frequently do by + * FirstLowInvalidHeapAttributeNumber. + */ +void +pull_child_partition_columns(Bitmapset **bitmapset, + Relation rel, + Relation parent) +{ + PartitionKey key = RelationGetPartitionKey(rel); + int16 partnatts = get_partition_natts(key); + List *partexprs = get_partition_exprs(key); + ListCell *lc; + Bitmapset *child_keycols = NULL; + int i; + AttrNumber *map; + int child_keycol = -1; + + /* + * First, compute the complete set of partition columns for this rel. For + * compatibility with the API exposed by pull_varattnos, we offset the + * column numbers by FirstLowInvalidHeapAttributeNumber. + */ + for (i = 0; i < partnatts; i++) + { + AttrNumber partattno = get_partition_col_attnum(key, i); + + if (partattno != 0) + child_keycols = + bms_add_member(child_keycols, + partattno - FirstLowInvalidHeapAttributeNumber); + } + foreach(lc, partexprs) + { + Node *expr = (Node *) lfirst(lc); + + pull_varattnos(expr, 1, &child_keycols); + } + + /* + * Next, work out how to convert from the attribute numbers for the child + * to the attribute numbers for the parent. + */ + map = + convert_tuples_by_name_map(RelationGetDescr(parent), + RelationGetDescr(rel), + gettext_noop("could not convert row type")); + + /* + * For each child key column we have identified, translate to the + * corresponding parent key column. Entry 0 in the map array corresponds + * to attribute number 1, which corresponds to a bitmapset entry for 1 - + * FirstLowInvalidHeapAttributeNumber. + */ + while ((child_keycol = bms_next_member(child_keycols, child_keycol)) >= 0) + { + int kc = child_keycol + FirstLowInvalidHeapAttributeNumber; + + Assert(kc > 0 && kc <= RelationGetNumberOfAttributes(rel)); + *bitmapset = + bms_add_member(*bitmapset, + map[kc - 1] - FirstLowInvalidHeapAttributeNumber); + } + + /* Release memory. */ + pfree(map); +} + +/* * qsort_partition_list_value_cmp * * Compare two list partition bound datums diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 8d17425..c15253f 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1794,6 +1794,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) ListCell *l; int i; Relation rel; + bool update_tuple_routing_needed = node->part_cols_updated; /* check for unsupported flags */ Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK))); @@ -1865,6 +1866,15 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) resultRelInfo->ri_IndexRelationDescs == NULL) ExecOpenIndices(resultRelInfo, mtstate->mt_onconflict != ONCONFLICT_NONE); + /* + * If this is an UPDATE and a BEFORE UPDATE trigger is present, we may + * need to do update tuple routing. + */ + if (resultRelInfo->ri_TrigDesc && + resultRelInfo->ri_TrigDesc->trig_update_before_row && + operation == CMD_UPDATE) + update_tuple_routing_needed = true; + /* Now init the plan for this result rel */ estate->es_result_relation_info = resultRelInfo; mtstate->mt_plans[i] = ExecInitNode(subplan, estate, eflags); @@ -1902,6 +1912,12 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) else rel = mtstate->resultRelInfo->ri_RelationDesc; + /* Decide whether we need to perform update tuple routing. */ + if (rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) + update_tuple_routing_needed = false; + if (update_tuple_routing_needed) + elog(NOTICE, "update tuple routing is needed"); + /* Build state for INSERT tuple routing */ if (operation == CMD_INSERT && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 67ac814..0c949a4 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -204,6 +204,7 @@ _copyModifyTable(const ModifyTable *from) COPY_SCALAR_FIELD(canSetTag); COPY_SCALAR_FIELD(nominalRelation); COPY_NODE_FIELD(partitioned_rels); + COPY_SCALAR_FIELD(part_cols_updated); COPY_NODE_FIELD(resultRelations); COPY_SCALAR_FIELD(resultRelIndex); COPY_SCALAR_FIELD(rootResultRelIndex); @@ -2256,6 +2257,7 @@ _copyPartitionedChildRelInfo(const PartitionedChildRelInfo *from) COPY_SCALAR_FIELD(parent_relid); COPY_NODE_FIELD(child_rels); + COPY_BITMAPSET_FIELD(all_part_cols); return newnode; } diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 91d64b7..15663d5 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -909,6 +909,7 @@ _equalPartitionedChildRelInfo(const PartitionedChildRelInfo *a, const Partitione { COMPARE_SCALAR_FIELD(parent_relid); COMPARE_NODE_FIELD(child_rels); + COMPARE_BITMAPSET_FIELD(all_part_cols); return true; } diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 3a23f0b..69b773f 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -349,6 +349,7 @@ _outModifyTable(StringInfo str, const ModifyTable *node) WRITE_BOOL_FIELD(canSetTag); WRITE_UINT_FIELD(nominalRelation); WRITE_NODE_FIELD(partitioned_rels); + WRITE_BOOL_FIELD(part_cols_updated); WRITE_NODE_FIELD(resultRelations); WRITE_INT_FIELD(resultRelIndex); WRITE_INT_FIELD(rootResultRelIndex); @@ -2067,6 +2068,7 @@ _outModifyTablePath(StringInfo str, const ModifyTablePath *node) WRITE_BOOL_FIELD(canSetTag); WRITE_UINT_FIELD(nominalRelation); WRITE_NODE_FIELD(partitioned_rels); + WRITE_BOOL_FIELD(part_cols_updated); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(subpaths); WRITE_NODE_FIELD(subroots); @@ -2489,6 +2491,7 @@ _outPartitionedChildRelInfo(StringInfo str, const PartitionedChildRelInfo *node) WRITE_UINT_FIELD(parent_relid); WRITE_NODE_FIELD(child_rels); + WRITE_BITMAPSET_FIELD(all_part_cols); } static void diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 2988e8b..0532add 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1547,6 +1547,7 @@ _readModifyTable(void) READ_BOOL_FIELD(canSetTag); READ_UINT_FIELD(nominalRelation); READ_NODE_FIELD(partitioned_rels); + READ_BOOL_FIELD(part_cols_updated); READ_NODE_FIELD(resultRelations); READ_INT_FIELD(resultRelIndex); READ_INT_FIELD(rootResultRelIndex); diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index f087ddb..064af0f 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -1291,7 +1291,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, rte = planner_rt_fetch(rel->relid, root); if (rte->relkind == RELKIND_PARTITIONED_TABLE) { - partitioned_rels = get_partitioned_child_rels(root, rel->relid); + partitioned_rels = get_partitioned_child_rels(root, rel->relid, NULL); /* The root partitioned table is included as a child rel */ Assert(list_length(partitioned_rels) >= 1); } diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index e589d92..7e4f058 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -277,6 +277,7 @@ static ProjectSet *make_project_set(List *tlist, Plan *subplan); static ModifyTable *make_modifytable(PlannerInfo *root, CmdType operation, bool canSetTag, Index nominalRelation, List *partitioned_rels, + bool part_cols_updated, List *resultRelations, List *subplans, List *withCheckOptionLists, List *returningLists, List *rowMarks, OnConflictExpr *onconflict, int epqParam); @@ -2357,6 +2358,7 @@ create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path) best_path->canSetTag, best_path->nominalRelation, best_path->partitioned_rels, + best_path->part_cols_updated, best_path->resultRelations, subplans, best_path->withCheckOptionLists, @@ -6398,6 +6400,7 @@ static ModifyTable * make_modifytable(PlannerInfo *root, CmdType operation, bool canSetTag, Index nominalRelation, List *partitioned_rels, + bool part_cols_updated, List *resultRelations, List *subplans, List *withCheckOptionLists, List *returningLists, List *rowMarks, OnConflictExpr *onconflict, int epqParam) @@ -6424,6 +6427,7 @@ make_modifytable(PlannerInfo *root, node->canSetTag = canSetTag; node->nominalRelation = nominalRelation; node->partitioned_rels = partitioned_rels; + node->part_cols_updated = part_cols_updated; node->resultRelations = resultRelations; node->resultRelIndex = -1; /* will be set correctly in setrefs.c */ node->rootResultRelIndex = -1; /* will be set correctly in setrefs.c */ diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 2988c11..bd99933 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -1042,6 +1042,7 @@ inheritance_planner(PlannerInfo *root) Index rti; RangeTblEntry *parent_rte; List *partitioned_rels = NIL; + bool part_cols_updated = false; Assert(parse->commandType != CMD_INSERT); @@ -1356,9 +1357,15 @@ inheritance_planner(PlannerInfo *root) if (parent_rte->relkind == RELKIND_PARTITIONED_TABLE) { - partitioned_rels = get_partitioned_child_rels(root, parentRTindex); + Bitmapset *all_part_cols; + + partitioned_rels = get_partitioned_child_rels(root, parentRTindex, + &all_part_cols); /* The root partitioned table is included as a child rel */ Assert(list_length(partitioned_rels) >= 1); + + if (bms_overlap(all_part_cols, parent_rte->updatedCols)) + part_cols_updated = true; } /* Result path must go into outer query's FINAL upperrel */ @@ -1415,6 +1422,7 @@ inheritance_planner(PlannerInfo *root) parse->canSetTag, nominalRelation, partitioned_rels, + part_cols_updated, resultRelations, subpaths, subroots, @@ -2032,6 +2040,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, parse->canSetTag, parse->resultRelation, NIL, + false, list_make1_int(parse->resultRelation), list_make1(path), list_make1(root), @@ -6062,10 +6071,15 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) * Returns a list of the RT indexes of the partitioned child relations * with rti as the root parent RT index. * + * If all_part_cols_p is non-NULL, *all_part_cols_p is set to a bitmapset + * of all partitioning columns used by the partitioned table or any + * descendent. + * * Note: Only call this function on RTEs known to be partitioned tables. */ List * -get_partitioned_child_rels(PlannerInfo *root, Index rti) +get_partitioned_child_rels(PlannerInfo *root, Index rti, + Bitmapset **all_part_cols_p) { List *result = NIL; ListCell *l; @@ -6077,6 +6091,8 @@ get_partitioned_child_rels(PlannerInfo *root, Index rti) if (pc->parent_relid == rti) { result = pc->child_rels; + if (all_part_cols_p) + *all_part_cols_p = pc->all_part_cols; break; } } diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index cf46b74..b52cf3b 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -33,6 +33,7 @@ #include "access/heapam.h" #include "access/htup_details.h" #include "access/sysattr.h" +#include "catalog/partition.h" #include "catalog/pg_inherits_fn.h" #include "catalog/pg_type.h" #include "miscadmin.h" @@ -1377,6 +1378,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) bool need_append; PartitionedChildRelInfo *pcinfo; List *partitioned_child_rels = NIL; + Bitmapset *all_part_cols = NULL; /* Does RT entry allow inheritance? */ if (!rte->inh) @@ -1535,8 +1537,12 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) } } else + { partitioned_child_rels = lappend_int(partitioned_child_rels, childRTindex); + pull_child_partition_columns(&all_part_cols, newrelation, + oldrelation); + } /* * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE. @@ -1604,6 +1610,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) Assert(rte->relkind == RELKIND_PARTITIONED_TABLE); pcinfo->parent_relid = rti; pcinfo->child_rels = partitioned_child_rels; + pcinfo->all_part_cols = all_part_cols; root->pcinfo_list = lappend(root->pcinfo_list, pcinfo); } diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index f2d6385..f63edf4 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -3161,6 +3161,8 @@ create_lockrows_path(PlannerInfo *root, RelOptInfo *rel, * 'partitioned_rels' is an integer list of RT indexes of non-leaf tables in * the partition tree, if this is an UPDATE/DELETE to a partitioned table. * Otherwise NIL. + * 'part_cols_updated' if any partitioning columns are being updated, either + * from the named relation or a descendent partitione table. * 'resultRelations' is an integer list of actual RT indexes of target rel(s) * 'subpaths' is a list of Path(s) producing source data (one per rel) * 'subroots' is a list of PlannerInfo structs (one per rel) @@ -3174,6 +3176,7 @@ ModifyTablePath * create_modifytable_path(PlannerInfo *root, RelOptInfo *rel, CmdType operation, bool canSetTag, Index nominalRelation, List *partitioned_rels, + bool part_cols_updated, List *resultRelations, List *subpaths, List *subroots, List *withCheckOptionLists, List *returningLists, @@ -3241,6 +3244,7 @@ create_modifytable_path(PlannerInfo *root, RelOptInfo *rel, pathnode->canSetTag = canSetTag; pathnode->nominalRelation = nominalRelation; pathnode->partitioned_rels = list_copy(partitioned_rels); + pathnode->part_cols_updated = part_cols_updated; pathnode->resultRelations = resultRelations; pathnode->subpaths = subpaths; pathnode->subroots = subroots; diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h index f10879a..058515e 100644 --- a/src/include/catalog/partition.h +++ b/src/include/catalog/partition.h @@ -98,4 +98,8 @@ extern int get_partition_for_tuple(PartitionDispatch *pd, EState *estate, PartitionDispatchData **failed_at, TupleTableSlot **failed_slot); +extern void pull_child_partition_columns(Bitmapset **bitmapset, + Relation rel, + Relation parent); + #endif /* PARTITION_H */ diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index f1a1b24..cd670b9 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -219,6 +219,7 @@ typedef struct ModifyTable Index nominalRelation; /* Parent RT index for use of EXPLAIN */ /* RT indexes of non-leaf tables in a partition tree */ List *partitioned_rels; + bool part_cols_updated; /* some part col in hierarchy updated */ List *resultRelations; /* integer list of RT indexes */ int resultRelIndex; /* index of first resultRel in plan's list */ int rootResultRelIndex; /* index of the partitioned table root */ diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 9bae3c6..3013964 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -1579,6 +1579,7 @@ typedef struct ModifyTablePath Index nominalRelation; /* Parent RT index for use of EXPLAIN */ /* RT indexes of non-leaf tables in a partition tree */ List *partitioned_rels; + bool part_cols_updated; /* some part col in hierarchy updated */ List *resultRelations; /* integer list of RT indexes */ List *subpaths; /* Path(s) producing source data */ List *subroots; /* per-target-table PlannerInfos */ @@ -2019,6 +2020,10 @@ typedef struct AppendRelInfo * The child_rels list must contain at least one element, because the parent * partitioned table is itself counted as a child. * + * all_part_cols contains all attribute numbers from the parent that are + * used as partitioning columns by the parent or some descendent which is + * itself partitioned. + * * These structs are kept in the PlannerInfo node's pcinfo_list. */ typedef struct PartitionedChildRelInfo @@ -2027,6 +2032,7 @@ typedef struct PartitionedChildRelInfo Index parent_relid; List *child_rels; + Bitmapset *all_part_cols; } PartitionedChildRelInfo; /* diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 0c0549d..d35f448 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -235,6 +235,7 @@ extern ModifyTablePath *create_modifytable_path(PlannerInfo *root, RelOptInfo *rel, CmdType operation, bool canSetTag, Index nominalRelation, List *partitioned_rels, + bool part_cols_updated, List *resultRelations, List *subpaths, List *subroots, List *withCheckOptionLists, List *returningLists, diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h index 2a4cf71..c6c15c5 100644 --- a/src/include/optimizer/planner.h +++ b/src/include/optimizer/planner.h @@ -57,6 +57,7 @@ extern Expr *preprocess_phv_expression(PlannerInfo *root, Expr *expr); extern bool plan_cluster_use_sort(Oid tableOid, Oid indexOid); -extern List *get_partitioned_child_rels(PlannerInfo *root, Index rti); +extern List *get_partitioned_child_rels(PlannerInfo *root, Index rti, + Bitmapset **all_part_cols_p); #endif /* PLANNER_H */