mirror of
https://github.com/postgres/postgres.git
synced 2025-10-31 10:30:33 +03:00
Allow UPDATE to move rows between partitions.
When an UPDATE causes a row to no longer match the partition constraint, try to move it to a different partition where it does match the partition constraint. In essence, the UPDATE is split into a DELETE from the old partition and an INSERT into the new one. This can lead to surprising behavior in concurrency scenarios because EvalPlanQual rechecks won't work as they normally did; the known problems are documented. (There is a pending patch to improve the situation further, but it needs more review.) Amit Khandekar, reviewed and tested by Amit Langote, David Rowley, Rajkumar Raghuwanshi, Dilip Kumar, Amul Sul, Thomas Munro, Álvaro Herrera, Amit Kapila, and me. A few final revisions by me. Discussion: http://postgr.es/m/CAJ3gD9do9o2ccQ7j7+tSgiE1REY65XRiMb=yJO3u3QhyP8EEPQ@mail.gmail.com
This commit is contained in:
@@ -1364,7 +1364,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
|
||||
case RTE_RELATION:
|
||||
if (rte->relkind == RELKIND_PARTITIONED_TABLE)
|
||||
partitioned_rels =
|
||||
get_partitioned_child_rels(root, rel->relid);
|
||||
get_partitioned_child_rels(root, rel->relid, NULL);
|
||||
break;
|
||||
case RTE_SUBQUERY:
|
||||
build_partitioned_rels = true;
|
||||
@@ -1403,7 +1403,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
|
||||
{
|
||||
List *cprels;
|
||||
|
||||
cprels = get_partitioned_child_rels(root, childrel->relid);
|
||||
cprels = get_partitioned_child_rels(root, childrel->relid, NULL);
|
||||
partitioned_rels = list_concat(partitioned_rels,
|
||||
list_copy(cprels));
|
||||
}
|
||||
|
||||
@@ -279,6 +279,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 partColsUpdated,
|
||||
List *resultRelations, List *subplans,
|
||||
List *withCheckOptionLists, List *returningLists,
|
||||
List *rowMarks, OnConflictExpr *onconflict, int epqParam);
|
||||
@@ -2373,6 +2374,7 @@ create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path)
|
||||
best_path->canSetTag,
|
||||
best_path->nominalRelation,
|
||||
best_path->partitioned_rels,
|
||||
best_path->partColsUpdated,
|
||||
best_path->resultRelations,
|
||||
subplans,
|
||||
best_path->withCheckOptionLists,
|
||||
@@ -6442,6 +6444,7 @@ static ModifyTable *
|
||||
make_modifytable(PlannerInfo *root,
|
||||
CmdType operation, bool canSetTag,
|
||||
Index nominalRelation, List *partitioned_rels,
|
||||
bool partColsUpdated,
|
||||
List *resultRelations, List *subplans,
|
||||
List *withCheckOptionLists, List *returningLists,
|
||||
List *rowMarks, OnConflictExpr *onconflict, int epqParam)
|
||||
@@ -6468,6 +6471,7 @@ make_modifytable(PlannerInfo *root,
|
||||
node->canSetTag = canSetTag;
|
||||
node->nominalRelation = nominalRelation;
|
||||
node->partitioned_rels = partitioned_rels;
|
||||
node->partColsUpdated = partColsUpdated;
|
||||
node->resultRelations = resultRelations;
|
||||
node->resultRelIndex = -1; /* will be set correctly in setrefs.c */
|
||||
node->rootResultRelIndex = -1; /* will be set correctly in setrefs.c */
|
||||
|
||||
@@ -1101,6 +1101,7 @@ inheritance_planner(PlannerInfo *root)
|
||||
Query *parent_parse;
|
||||
Bitmapset *parent_relids = bms_make_singleton(top_parentRTindex);
|
||||
PlannerInfo **parent_roots = NULL;
|
||||
bool partColsUpdated = false;
|
||||
|
||||
Assert(parse->commandType != CMD_INSERT);
|
||||
|
||||
@@ -1172,7 +1173,8 @@ inheritance_planner(PlannerInfo *root)
|
||||
if (parent_rte->relkind == RELKIND_PARTITIONED_TABLE)
|
||||
{
|
||||
nominalRelation = top_parentRTindex;
|
||||
partitioned_rels = get_partitioned_child_rels(root, top_parentRTindex);
|
||||
partitioned_rels = get_partitioned_child_rels(root, top_parentRTindex,
|
||||
&partColsUpdated);
|
||||
/* The root partitioned table is included as a child rel */
|
||||
Assert(list_length(partitioned_rels) >= 1);
|
||||
}
|
||||
@@ -1512,6 +1514,7 @@ inheritance_planner(PlannerInfo *root)
|
||||
parse->canSetTag,
|
||||
nominalRelation,
|
||||
partitioned_rels,
|
||||
partColsUpdated,
|
||||
resultRelations,
|
||||
subpaths,
|
||||
subroots,
|
||||
@@ -2123,6 +2126,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),
|
||||
@@ -6155,17 +6159,24 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid)
|
||||
/*
|
||||
* get_partitioned_child_rels
|
||||
* Returns a list of the RT indexes of the partitioned child relations
|
||||
* with rti as the root parent RT index.
|
||||
* with rti as the root parent RT index. Also sets
|
||||
* *part_cols_updated to true if any of the root rte's updated
|
||||
* columns is used in the partition key either of the relation whose RTI
|
||||
* is specified or of any child relation.
|
||||
*
|
||||
* Note: This function might get called even for range table entries that
|
||||
* are not partitioned tables; in such a case, it will simply return NIL.
|
||||
*/
|
||||
List *
|
||||
get_partitioned_child_rels(PlannerInfo *root, Index rti)
|
||||
get_partitioned_child_rels(PlannerInfo *root, Index rti,
|
||||
bool *part_cols_updated)
|
||||
{
|
||||
List *result = NIL;
|
||||
ListCell *l;
|
||||
|
||||
if (part_cols_updated)
|
||||
*part_cols_updated = false;
|
||||
|
||||
foreach(l, root->pcinfo_list)
|
||||
{
|
||||
PartitionedChildRelInfo *pc = lfirst_node(PartitionedChildRelInfo, l);
|
||||
@@ -6173,6 +6184,8 @@ get_partitioned_child_rels(PlannerInfo *root, Index rti)
|
||||
if (pc->parent_relid == rti)
|
||||
{
|
||||
result = pc->child_rels;
|
||||
if (part_cols_updated)
|
||||
*part_cols_updated = pc->part_cols_updated;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root,
|
||||
RangeTblEntry *parentrte,
|
||||
Index parentRTindex, Relation parentrel,
|
||||
PlanRowMark *top_parentrc, LOCKMODE lockmode,
|
||||
List **appinfos, List **partitioned_child_rels);
|
||||
List **appinfos, List **partitioned_child_rels,
|
||||
bool *part_cols_updated);
|
||||
static void expand_single_inheritance_child(PlannerInfo *root,
|
||||
RangeTblEntry *parentrte,
|
||||
Index parentRTindex, Relation parentrel,
|
||||
@@ -1461,16 +1462,19 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
|
||||
if (RelationGetPartitionDesc(oldrelation) != NULL)
|
||||
{
|
||||
List *partitioned_child_rels = NIL;
|
||||
bool part_cols_updated = false;
|
||||
|
||||
Assert(rte->relkind == RELKIND_PARTITIONED_TABLE);
|
||||
|
||||
/*
|
||||
* If this table has partitions, recursively expand them in the order
|
||||
* in which they appear in the PartitionDesc.
|
||||
* in which they appear in the PartitionDesc. While at it, also
|
||||
* extract the partition key columns of all the partitioned tables.
|
||||
*/
|
||||
expand_partitioned_rtentry(root, rte, rti, oldrelation, oldrc,
|
||||
lockmode, &root->append_rel_list,
|
||||
&partitioned_child_rels);
|
||||
&partitioned_child_rels,
|
||||
&part_cols_updated);
|
||||
|
||||
/*
|
||||
* We keep a list of objects in root, each of which maps a root
|
||||
@@ -1487,6 +1491,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
|
||||
pcinfo = makeNode(PartitionedChildRelInfo);
|
||||
pcinfo->parent_relid = rti;
|
||||
pcinfo->child_rels = partitioned_child_rels;
|
||||
pcinfo->part_cols_updated = part_cols_updated;
|
||||
root->pcinfo_list = lappend(root->pcinfo_list, pcinfo);
|
||||
}
|
||||
}
|
||||
@@ -1563,7 +1568,8 @@ static void
|
||||
expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
|
||||
Index parentRTindex, Relation parentrel,
|
||||
PlanRowMark *top_parentrc, LOCKMODE lockmode,
|
||||
List **appinfos, List **partitioned_child_rels)
|
||||
List **appinfos, List **partitioned_child_rels,
|
||||
bool *part_cols_updated)
|
||||
{
|
||||
int i;
|
||||
RangeTblEntry *childrte;
|
||||
@@ -1578,6 +1584,17 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
|
||||
|
||||
Assert(parentrte->inh);
|
||||
|
||||
/*
|
||||
* Note down whether any partition key cols are being updated. Though it's
|
||||
* the root partitioned table's updatedCols we are interested in, we
|
||||
* instead use parentrte to get the updatedCols. This is convenient because
|
||||
* parentrte already has the root partrel's updatedCols translated to match
|
||||
* the attribute ordering of parentrel.
|
||||
*/
|
||||
if (!*part_cols_updated)
|
||||
*part_cols_updated =
|
||||
has_partition_attrs(parentrel, parentrte->updatedCols, NULL);
|
||||
|
||||
/* First expand the partitioned table itself. */
|
||||
expand_single_inheritance_child(root, parentrte, parentRTindex, parentrel,
|
||||
top_parentrc, parentrel,
|
||||
@@ -1617,7 +1634,8 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
|
||||
if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
|
||||
expand_partitioned_rtentry(root, childrte, childRTindex,
|
||||
childrel, top_parentrc, lockmode,
|
||||
appinfos, partitioned_child_rels);
|
||||
appinfos, partitioned_child_rels,
|
||||
part_cols_updated);
|
||||
|
||||
/* Close child relation, but keep locks */
|
||||
heap_close(childrel, NoLock);
|
||||
|
||||
@@ -3274,6 +3274,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.
|
||||
* 'partColsUpdated' is true if any partitioning columns are being updated,
|
||||
* either from the target relation or a descendent partitioned 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)
|
||||
@@ -3287,6 +3289,7 @@ ModifyTablePath *
|
||||
create_modifytable_path(PlannerInfo *root, RelOptInfo *rel,
|
||||
CmdType operation, bool canSetTag,
|
||||
Index nominalRelation, List *partitioned_rels,
|
||||
bool partColsUpdated,
|
||||
List *resultRelations, List *subpaths,
|
||||
List *subroots,
|
||||
List *withCheckOptionLists, List *returningLists,
|
||||
@@ -3354,6 +3357,7 @@ create_modifytable_path(PlannerInfo *root, RelOptInfo *rel,
|
||||
pathnode->canSetTag = canSetTag;
|
||||
pathnode->nominalRelation = nominalRelation;
|
||||
pathnode->partitioned_rels = list_copy(partitioned_rels);
|
||||
pathnode->partColsUpdated = partColsUpdated;
|
||||
pathnode->resultRelations = resultRelations;
|
||||
pathnode->subpaths = subpaths;
|
||||
pathnode->subroots = subroots;
|
||||
|
||||
Reference in New Issue
Block a user