update-partition-key_v3.patch
application/octet-stream
Filename: update-partition-key_v3.patch
Type: application/octet-stream
Part: 0
Message:
Re: UPDATE of partition key
Patch
Format: unified
Series: patch v3
| File | + | − |
|---|---|---|
| src/backend/executor/execMain.c | 1 | 1 |
| src/backend/executor/nodeModifyTable.c | 141 | 32 |
| src/include/executor/executor.h | 3 | 0 |
| src/test/regress/expected/update.out | 2 | 3 |
| src/test/regress/sql/update.sql | 2 | 1 |
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index c28cf9c..2878ff3 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1778,7 +1778,7 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
*
* Note: This is called *iff* resultRelInfo is the main target table.
*/
-static bool
+bool
ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
EState *estate)
{
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 29c6a6e..287af13 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -62,7 +62,7 @@ static bool ExecOnConflictUpdate(ModifyTableState *mtstate,
EState *estate,
bool canSetTag,
TupleTableSlot **returning);
-
+static void ExecInitPartitionReturningProjection(ModifyTableState *mtstate, Relation root_rel);
/*
* Verify that the tuples to be produced by INSERT or UPDATE match the
* target relation's rowtype
@@ -625,6 +625,7 @@ ExecDelete(ItemPointer tupleid,
TupleTableSlot *planSlot,
EPQState *epqstate,
EState *estate,
+ bool *concurrently_deleted,
bool canSetTag)
{
ResultRelInfo *resultRelInfo;
@@ -633,6 +634,9 @@ ExecDelete(ItemPointer tupleid,
HeapUpdateFailureData hufd;
TupleTableSlot *slot = NULL;
+ if (concurrently_deleted)
+ *concurrently_deleted = false;
+
/*
* get information on the (current) result relation
*/
@@ -776,6 +780,8 @@ ldelete:;
}
}
/* tuple already deleted; nothing to do */
+ if (concurrently_deleted)
+ *concurrently_deleted = true;
return NULL;
default:
@@ -878,7 +884,8 @@ ldelete:;
* ----------------------------------------------------------------
*/
static TupleTableSlot *
-ExecUpdate(ItemPointer tupleid,
+ExecUpdate(ModifyTableState *mtstate,
+ ItemPointer tupleid,
HeapTuple oldtuple,
TupleTableSlot *slot,
TupleTableSlot *planSlot,
@@ -987,6 +994,69 @@ lreplace:;
ExecWithCheckOptions(WCO_RLS_UPDATE_CHECK,
resultRelInfo, slot, estate);
+ if (resultRelInfo->ri_PartitionCheck &&
+ !ExecPartitionCheck(resultRelInfo, slot, estate))
+ {
+ bool is_partitioned_table = true;
+
+ if (!mtstate->mt_partition_dispatch_info)
+ {
+ ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
+ Relation root_rel;
+
+ /* root table RT index is at the head of partitioned_rels */
+ if (node->partitioned_rels)
+ {
+ Index root_rti;
+ Oid root_oid;
+
+ root_rti = linitial_int(node->partitioned_rels);
+ root_oid = getrelid(root_rti, estate->es_range_table);
+ root_rel = heap_open(root_oid, NoLock); /* locked by InitPlan */
+ }
+ else
+ root_rel = mtstate->resultRelInfo->ri_RelationDesc;
+
+ is_partitioned_table =
+ root_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE;
+
+ if (is_partitioned_table)
+ ExecSetupPartitionTupleRouting(
+ root_rel,
+ &mtstate->mt_partition_dispatch_info,
+ &mtstate->mt_partitions,
+ &mtstate->mt_partition_tupconv_maps,
+ &mtstate->mt_partition_tuple_slot,
+ &mtstate->mt_num_dispatch,
+ &mtstate->mt_num_partitions);
+
+ /* Build a projection for each leaf partition rel. */
+ ExecInitPartitionReturningProjection(mtstate, root_rel);
+ }
+
+ /*
+ * If it's not a partitioned table after all, let it fall through
+ * the usual error handling.
+ */
+ if (is_partitioned_table)
+ {
+ bool concurrently_deleted;
+
+ ExecDelete(tupleid, oldtuple, planSlot, epqstate, estate,
+ &concurrently_deleted, canSetTag);
+
+ if (concurrently_deleted)
+ return NULL;
+
+ /*
+ * Don't update estate.es_processed updated again. ExecDelete()
+ * has already done it above. So use canSetTag=false.
+ */
+ return ExecInsert(mtstate, slot, planSlot, NULL,
+ ONCONFLICT_NONE, estate, false);
+ }
+ }
+
/*
* Check the constraints of the tuple. Note that we pass the same
* slot for the orig_slot argument, because unlike ExecInsert(), no
@@ -1313,7 +1383,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
*/
/* Execute UPDATE with projection */
- *returning = ExecUpdate(&tuple.t_self, NULL,
+ *returning = ExecUpdate(mtstate, &tuple.t_self, NULL,
mtstate->mt_conflproj, planSlot,
&mtstate->mt_epqstate, mtstate->ps.state,
canSetTag);
@@ -1583,12 +1653,12 @@ ExecModifyTable(ModifyTableState *node)
estate, node->canSetTag);
break;
case CMD_UPDATE:
- slot = ExecUpdate(tupleid, oldtuple, slot, planSlot,
+ slot = ExecUpdate(node, tupleid, oldtuple, slot, planSlot,
&node->mt_epqstate, estate, node->canSetTag);
break;
case CMD_DELETE:
slot = ExecDelete(tupleid, oldtuple, planSlot,
- &node->mt_epqstate, estate, node->canSetTag);
+ &node->mt_epqstate, estate, NULL, node->canSetTag);
break;
default:
elog(ERROR, "unknown operation");
@@ -1837,7 +1907,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
{
TupleTableSlot *slot;
ExprContext *econtext;
- List *returningList;
/*
* Initialize result tuple slot and assign its rowtype using the first
@@ -1872,30 +1941,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
}
/*
- * Build a projection for each leaf partition rel. Note that we
- * didn't build the returningList for each partition within the
- * planner, but simple translation of the varattnos for each partition
- * will suffice. This only occurs for the INSERT case; UPDATE/DELETE
- * are handled above.
+ * Build a projection for each leaf partition rel. This only occurs for
+ * the INSERT case; UPDATE/DELETE are handled above.
*/
- resultRelInfo = mtstate->mt_partitions;
- returningList = linitial(node->returningLists);
- for (i = 0; i < mtstate->mt_num_partitions; i++)
- {
- Relation partrel = resultRelInfo->ri_RelationDesc;
- List *rlist,
- *rliststate;
-
- /* varno = node->nominalRelation */
- rlist = map_partition_varattnos(returningList,
- node->nominalRelation,
- partrel, rel);
- rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
- resultRelInfo->ri_projectReturning =
- ExecBuildProjectionInfo(rliststate, econtext, slot,
- resultRelInfo->ri_RelationDesc->rd_att);
- resultRelInfo++;
- }
+ ExecInitPartitionReturningProjection(mtstate, rel);
}
else
{
@@ -2124,6 +2173,56 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
}
/* ----------------------------------------------------------------
+ * ExecInitPartitionReturningProjection
+ *
+ * Initialize stuff required to handle RETURNING for leaf partitions.
+ * We don't build the returningList for each partition within the planner, but
+ * simple translation of the varattnos for each partition suffices. This
+ * actually is helpful only for INSERT case; UPDATE/DELETE are handled
+ * differently.
+ * ----------------------------------------------------------------
+ */
+static void
+ExecInitPartitionReturningProjection(ModifyTableState *mtstate, Relation root_rel)
+{
+ ResultRelInfo *resultRelInfo = mtstate->mt_partitions;
+ ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
+ TupleTableSlot *returning_slot = mtstate->ps.ps_ResultTupleSlot;
+ List *returningList;
+ int i;
+
+ /*
+ * If there is no returning clause, or if we have already initialized the
+ * returning projection info, there is nothing to be done.
+ */
+ if (node->returningLists == NIL ||
+ (resultRelInfo && resultRelInfo->ri_projectReturning != NULL) ||
+ mtstate->mt_num_partitions == 0)
+ return;
+
+ returningList = linitial(node->returningLists);
+ for (i = 0; i < mtstate->mt_num_partitions; i++)
+ {
+ Relation partrel = resultRelInfo->ri_RelationDesc;
+ List *rlist,
+ *rliststate;
+
+ /* varno = node->nominalRelation */
+ rlist = map_partition_varattnos(returningList,
+ node->nominalRelation,
+ partrel, root_rel);
+ rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
+ resultRelInfo->ri_projectReturning =
+ ExecBuildProjectionInfo(rliststate,
+ mtstate->ps.ps_ExprContext,
+ returning_slot,
+ resultRelInfo->ri_RelationDesc->rd_att);
+ resultRelInfo++;
+ }
+}
+
+
+/* ----------------------------------------------------------------
* ExecEndModifyTable
*
* Shuts down the plan.
@@ -2154,10 +2253,19 @@ ExecEndModifyTable(ModifyTableState *node)
* Close all the partitioned tables, leaf partitions, and their indices
*
* Remember node->mt_partition_dispatch_info[0] corresponds to the root
- * partitioned table, which we must not try to close, because it is the
- * main target table of the query that will be closed by ExecEndPlan().
- * Also, tupslot is NULL for the root partitioned table.
+ * partitioned table, which should not be closed if it is the main target
+ * table of the query, which will be closed by ExecEndPlan(). Also, tupslot
+ * is NULL for the root partitioned table.
*/
+ if (node->mt_num_dispatch > 0)
+ {
+ Relation root_partition;
+
+ root_partition = node->mt_partition_dispatch_info[0]->reldesc;
+ if (root_partition != node->resultRelInfo->ri_RelationDesc)
+ heap_close(root_partition, NoLock);
+ }
+
for (i = 1; i < node->mt_num_dispatch; i++)
{
PartitionDispatch pd = node->mt_partition_dispatch_info[i];
@@ -2165,6 +2273,7 @@ ExecEndModifyTable(ModifyTableState *node)
heap_close(pd->reldesc, NoLock);
ExecDropSingleTupleTableSlot(pd->tupslot);
}
+
for (i = 0; i < node->mt_num_partitions; i++)
{
ResultRelInfo *resultRelInfo = node->mt_partitions + i;
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index a5c75e7..1fc7cb2 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -225,6 +225,9 @@ extern int ExecFindPartition(ResultRelInfo *resultRelInfo,
PartitionDispatch *pd,
TupleTableSlot *slot,
EState *estate);
+extern bool ExecPartitionCheck(ResultRelInfo *resultRelInfo,
+ TupleTableSlot *slot,
+ EState *estate);
#define EvalPlanQualSetSlot(epqstate, slot) ((epqstate)->origslot = (slot))
extern void EvalPlanQualFetchRowMarks(EPQState *epqstate);
diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out
index 9366f04..99c8046 100644
--- a/src/test/regress/expected/update.out
+++ b/src/test/regress/expected/update.out
@@ -209,13 +209,12 @@ create table part_b_1_b_10 partition of range_parted for values from ('b', 1) to
create table part_b_10_b_20 partition of range_parted for values from ('b', 10) to ('b', 20);
insert into part_a_1_a_10 values ('a', 1);
insert into part_b_10_b_20 values ('b', 10);
--- fail
+-- fail (row movement happens only within the partition subtree)
update part_a_1_a_10 set a = 'b' where a = 'a';
ERROR: new row for relation "part_a_1_a_10" violates partition constraint
DETAIL: Failing row contains (b, 1).
+-- ok (row movement)
update range_parted set b = b - 1 where b = 10;
-ERROR: new row for relation "part_b_10_b_20" violates partition constraint
-DETAIL: Failing row contains (b, 9).
-- ok
update range_parted set b = b + 1 where b = 10;
-- cleanup
diff --git a/src/test/regress/sql/update.sql b/src/test/regress/sql/update.sql
index 6637119..7667793 100644
--- a/src/test/regress/sql/update.sql
+++ b/src/test/regress/sql/update.sql
@@ -119,8 +119,9 @@ create table part_b_10_b_20 partition of range_parted for values from ('b', 10)
insert into part_a_1_a_10 values ('a', 1);
insert into part_b_10_b_20 values ('b', 10);
--- fail
+-- fail (row movement happens only within the partition subtree)
update part_a_1_a_10 set a = 'b' where a = 'a';
+-- ok (row movement)
update range_parted set b = b - 1 where b = 10;
-- ok
update range_parted set b = b + 1 where b = 10;