agora inbox for pgsql-hackers@postgresql.org  
help / color / mirror / Atom feed
[PATCH v1] fix ON CONFLICT DO UPDATE for partitioned tables
2+ messages / 2 participants
[nested] [flat]

* [PATCH v1] fix ON CONFLICT DO UPDATE for partitioned tables
@ 2018-02-27 23:52  Alvaro Herrera <alvherre@alvh.no-ip.org>
  0 siblings, 0 replies; 2+ messages in thread

From: Alvaro Herrera @ 2018-02-27 23:52 UTC (permalink / raw)

---
 src/backend/catalog/pg_inherits.c             |  72 ++++++++++++++++
 src/backend/executor/execPartition.c          |  29 +++++++
 src/backend/executor/nodeModifyTable.c        |  33 +++++++-
 src/backend/optimizer/util/plancat.c          |   3 +-
 src/backend/parser/analyze.c                  |   7 --
 src/include/catalog/pg_inherits_fn.h          |   3 +
 src/test/regress/expected/insert_conflict.out | 113 ++++++++++++++++++++++++--
 src/test/regress/sql/insert_conflict.sql      |  75 +++++++++++++++--
 8 files changed, 311 insertions(+), 24 deletions(-)

diff --git a/src/backend/catalog/pg_inherits.c b/src/backend/catalog/pg_inherits.c
index 5a5beb9273..e1a46bcd2b 100644
--- a/src/backend/catalog/pg_inherits.c
+++ b/src/backend/catalog/pg_inherits.c
@@ -407,6 +407,78 @@ typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId)
 }
 
 /*
+ * Given a list of index OIDs in the rootrel, return a list of OIDs of the
+ * corresponding indexes in the partrel.  If any index in the rootrel does not
+ * correspond to any index in the child, an error is raised.
+ *
+ * This processes the index list for INSERT ON CONFLICT DO UPDATE at execution
+ * time.  This fact is hardcoded in the error messages.
+ *
+ * XXX this implementation fails if the partition is not a direct child of
+ * rootrel.
+ */
+List *
+MapPartitionIndexList(Relation rootrel, Relation partrel, List *indexlist)
+{
+	List	   *result = NIL;
+	List	   *partIdxs;
+	Relation	inhRel;
+	ScanKeyData	key;
+	ListCell   *cell;
+
+	partIdxs = RelationGetIndexList(partrel);
+	/* quick exit if partition has no indexes */
+	if (partIdxs == NIL)
+		return NIL;
+
+	inhRel = heap_open(InheritsRelationId, AccessShareLock);
+
+	foreach(cell, indexlist)
+	{
+		Oid			parentIdx = lfirst_oid(cell);
+		SysScanDesc	scan;
+		HeapTuple	tuple;
+		bool		found = false;
+
+		ScanKeyInit(&key,
+					Anum_pg_inherits_inhparent,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(parentIdx));
+
+		scan = systable_beginscan(inhRel, InheritsParentIndexId, true,
+								  NULL, 1, &key);
+		while (HeapTupleIsValid(tuple = systable_getnext(scan)))
+		{
+			Oid indexoid = ((Form_pg_inherits) GETSTRUCT(tuple))->inhrelid;
+
+			if (list_member_oid(partIdxs, indexoid))
+			{
+				result = lappend_oid(result, indexoid);
+				found = true;
+				break;
+			}
+		}
+		systable_endscan(scan);
+
+		/*
+		 * Indexes can only be used as inference targets if they exist in the
+		 * partition that receives the tuple; bail out if we cannot find it.
+		 */
+		if (!found)
+			ereport(ERROR,
+					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+					 errmsg("invalid ON CONFLICT DO UPDATE specification"),
+					 errdetail("An inferred index was not found in partition \"%s\".",
+							   RelationGetRelationName(partrel))));
+	}
+
+	relation_close(inhRel, AccessShareLock);
+	list_free(partIdxs);
+
+	return result;
+}
+
+/*
  * Create a single pg_inherits row with the given data
  */
 void
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index 54efc9e545..95a814e975 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -475,6 +475,35 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
 									&mtstate->ps, RelationGetDescr(partrel));
 	}
 
+	/*
+	 * If needed, initialize projection and qual for ON CONFLICT DO UPDATE for
+	 * this partition.
+	 */
+	if (node && node->onConflictAction == ONCONFLICT_UPDATE)
+	{
+		ExprContext *econtext = mtstate->ps.ps_ExprContext;
+		List	   *leaf_oc_set;
+
+		leaf_oc_set = map_partition_varattnos(node->onConflictSet,
+											  node->nominalRelation,
+											  partrel, rootrel, NULL);
+		leaf_part_rri->ri_onConflictSetProj =
+			ExecBuildProjectionInfo(leaf_oc_set, econtext,
+									mtstate->mt_conflproj, &mtstate->ps,
+									RelationGetDescr(partrel));
+		if (node->onConflictWhere)
+		{
+			List	   *leaf_oc_where;
+
+			leaf_oc_where =
+				map_partition_varattnos((List *) node->onConflictWhere,
+										node->nominalRelation,
+										partrel, rootrel, NULL);
+			leaf_part_rri->ri_onConflictSetWhere =
+				ExecInitQual(leaf_oc_where, &mtstate->ps);
+		}
+	}
+
 	Assert(proute->partitions[partidx] == NULL);
 	proute->partitions[partidx] = leaf_part_rri;
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index c32928d9bd..9748f80ddc 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -39,6 +39,7 @@
 
 #include "access/htup_details.h"
 #include "access/xact.h"
+#include "catalog/pg_inherits_fn.h"
 #include "commands/trigger.h"
 #include "executor/execPartition.h"
 #include "executor/executor.h"
@@ -510,6 +511,20 @@ ExecInsert(ModifyTableState *mtstate,
 			uint32		specToken;
 			ItemPointerData conflictTid;
 			bool		specConflict;
+			List	   *mappedArbiterIndexes;
+
+			/*
+			 * Map the arbiter index list to the OIDs in the corresponding
+			 * partition.
+			 */
+			if (saved_resultRelInfo &&
+				resultRelInfo->ri_RelationDesc->rd_rel->relispartition)
+				mappedArbiterIndexes =
+					MapPartitionIndexList(saved_resultRelInfo->ri_RelationDesc,
+										  resultRelInfo->ri_RelationDesc,
+										  arbiterIndexes);
+			else
+				mappedArbiterIndexes = arbiterIndexes;
 
 			/*
 			 * Do a non-conclusive check for conflicts first.
@@ -526,7 +541,7 @@ ExecInsert(ModifyTableState *mtstate,
 	vlock:
 			specConflict = false;
 			if (!ExecCheckIndexConstraints(slot, estate, &conflictTid,
-										   arbiterIndexes))
+										   mappedArbiterIndexes))
 			{
 				/* committed conflict tuple found */
 				if (onconflict == ONCONFLICT_UPDATE)
@@ -581,7 +596,7 @@ ExecInsert(ModifyTableState *mtstate,
 			/* insert index entries for tuple */
 			recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
 												   estate, true, &specConflict,
-												   arbiterIndexes);
+												   mappedArbiterIndexes);
 
 			/* adjust the tuple's state accordingly */
 			if (!specConflict)
@@ -1146,6 +1161,18 @@ lreplace:;
 			TupleConversionMap *tupconv_map;
 
 			/*
+			 * Disallow an INSERT ON CONFLICT DO UPDATE that causes the
+			 * original row to migrate to a different partition.  Maybe this
+			 * can be implemented some day, but it seems a fringe feature with
+			 * little redeeming value.
+			 */
+			if (((ModifyTable *) mtstate->ps.plan)->onConflictAction == ONCONFLICT_UPDATE)
+				ereport(ERROR,
+						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+						 errmsg("invalid ON UPDATE specification"),
+						 errdetail("The result tuple would appear in a different partition than the original tuple.")));
+
+			/*
 			 * When an UPDATE is run on a leaf partition, we will not have
 			 * partition tuple routing set up. In that case, fail with
 			 * partition constraint violation error.
@@ -2329,7 +2356,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	}
 
 	/*
-	 * If needed, Initialize target list, projection and qual for ON CONFLICT
+	 * If needed, initialize target list, projection and qual for ON CONFLICT
 	 * DO UPDATE.
 	 */
 	resultRelInfo = mtstate->resultRelInfo;
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 60f21711f4..db7c0030ca 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -558,7 +558,8 @@ get_relation_foreign_keys(PlannerInfo *root, RelOptInfo *rel,
 
 /*
  * infer_arbiter_indexes -
- *	  Determine the unique indexes used to arbitrate speculative insertion.
+ *	  Determine the unique indexes used to arbitrate speculative insertion,
+ *	  and return them as a list of OIDs.
  *
  * Uses user-supplied inference clause expressions and predicate to match a
  * unique index from those defined and ready on the heap relation (target).
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index c3a9617f67..92696f0607 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -1025,13 +1025,6 @@ transformOnConflictClause(ParseState *pstate,
 		TargetEntry *te;
 		int			attno;
 
-		if (targetrel->rd_partdesc)
-			ereport(ERROR,
-					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-					 errmsg("%s cannot be applied to partitioned table \"%s\"",
-							"ON CONFLICT DO UPDATE",
-							RelationGetRelationName(targetrel))));
-
 		/*
 		 * All INSERT expressions have been parsed, get ready for potentially
 		 * existing SET statements that need to be processed like an UPDATE.
diff --git a/src/include/catalog/pg_inherits_fn.h b/src/include/catalog/pg_inherits_fn.h
index eebee977a5..20fb96db51 100644
--- a/src/include/catalog/pg_inherits_fn.h
+++ b/src/include/catalog/pg_inherits_fn.h
@@ -16,6 +16,7 @@
 
 #include "nodes/pg_list.h"
 #include "storage/lock.h"
+#include "utils/relcache.h"
 
 extern List *find_inheritance_children(Oid parentrelId, LOCKMODE lockmode);
 extern List *find_all_inheritors(Oid parentrelId, LOCKMODE lockmode,
@@ -23,6 +24,8 @@ extern List *find_all_inheritors(Oid parentrelId, LOCKMODE lockmode,
 extern bool has_subclass(Oid relationId);
 extern bool has_superclass(Oid relationId);
 extern bool typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId);
+extern List *MapPartitionIndexList(Relation rootrel, Relation partrel,
+					  List *indexlist);
 extern void StoreSingleInheritance(Oid relationId, Oid parentOid,
 					   int32 seqNumber);
 extern bool DeleteInheritsTuple(Oid inhrelid, Oid inhparent);
diff --git a/src/test/regress/expected/insert_conflict.out b/src/test/regress/expected/insert_conflict.out
index 2650faedee..da8fe11120 100644
--- a/src/test/regress/expected/insert_conflict.out
+++ b/src/test/regress/expected/insert_conflict.out
@@ -786,16 +786,115 @@ select * from selfconflict;
 (3 rows)
 
 drop table selfconflict;
--- check that the following works:
+--
+-- INSERT ON CONFLICT and partitioned tables
+--
+-- DO NOTHING works
 -- insert into partitioned_table on conflict do nothing
 create table parted_conflict_test (a int, b char) partition by list (a);
 create table parted_conflict_test_1 partition of parted_conflict_test (b unique) for values in (1);
 insert into parted_conflict_test values (1, 'a') on conflict do nothing;
 insert into parted_conflict_test values (1, 'a') on conflict do nothing;
--- however, on conflict do update is not supported yet
-insert into parted_conflict_test values (1) on conflict (b) do update set a = excluded.a;
-ERROR:  ON CONFLICT DO UPDATE cannot be applied to partitioned table "parted_conflict_test"
--- but it works OK if we target the partition directly
-insert into parted_conflict_test_1 values (1) on conflict (b) do
-update set a = excluded.a;
+drop table parted_conflict_test;
+-- simple DO UPDATE works, as long as the tuple remains in the same partition
+create table parted_conflict_test (a int primary key, b text) partition by list (a);
+create table parted_conflict_test_1 partition of parted_conflict_test for values in (1, 2);
+create table parted_conflict_test_2 partition of parted_conflict_test for values in (3, 4);
+insert into parted_conflict_test values (1, 'first');
+insert into parted_conflict_test values (1, 'second')
+  on conflict (a) do nothing;
+insert into parted_conflict_test values (1, 'third')
+  on conflict (a) do update set b = format('%s (was %s)', excluded.b, parted_conflict_test.b);
+select * from parted_conflict_test;
+ a |         b         
+---+-------------------
+ 1 | third (was first)
+(1 row)
+
+insert into parted_conflict_test values (1, 'b')
+  on conflict (a) do update set b = 'fourth'
+  where parted_conflict_test.b = 'third (was first)';
+select * from parted_conflict_test;
+ a |   b    
+---+--------
+ 1 | fourth
+(1 row)
+
+insert into parted_conflict_test values (1, 'c')
+  on conflict (a) do update set b = 'fourth'
+  where parted_conflict_test.b = 'b';
+select * from parted_conflict_test;
+ a |   b    
+---+--------
+ 1 | fourth
+(1 row)
+
+insert into parted_conflict_test values (1, 'fifth')
+  on conflict (a) do update set a = parted_conflict_test.a * 2,
+  b = format('%s (was %s)', excluded.b, parted_conflict_test.b);
+select * from parted_conflict_test;
+ a |         b          
+---+--------------------
+ 2 | fifth (was fourth)
+(1 row)
+
+-- targetting the partition directly also works
+insert into parted_conflict_test_1 values (2, 'sixth') on conflict (a) do
+  update set b = format('%s (was %s)', excluded.b, parted_conflict_test_1.b);
+select * from parted_conflict_test;
+ a |               b                
+---+--------------------------------
+ 2 | sixth (was fifth (was fourth))
+(1 row)
+
+drop table parted_conflict_test;
+-- moving tuple to another partition in the UPDATE clause is not supported
+create table parted_conflict_test (a int, b text) partition by list (a);
+create table parted_conflict_test_1 partition of parted_conflict_test for values in (1);
+create table parted_conflict_test_2 partition of parted_conflict_test for values in (2);
+insert into parted_conflict_test values (1, 'one');
+insert into parted_conflict_test values (1, 'one two')
+  on conflict (a) do update set a = excluded.a * 2;
+ERROR:  there is no unique or exclusion constraint matching the ON CONFLICT specification
+drop table parted_conflict_test;
+-- multiple-layered partitioning
+create table parted_conflict_test (a int primary key, b text) partition by range (a);
+create table parted_conflict_test_1 partition of parted_conflict_test
+  for values from (0) to (10000) partition by range (a);
+create table parted_conflict_test_1_1 partition of parted_conflict_test_1
+  for values from (0) to (100);
+insert into parted_conflict_test values ('10', 'ten');
+insert into parted_conflict_test values ('10', 'ten two')
+  on conflict (a) do update set b = excluded.b;
+ERROR:  invalid ON CONFLICT DO UPDATE specification
+DETAIL:  An inferred index was not found in partition "parted_conflict_test_1_1".
+select * from parted_conflict_test;
+ a  |  b  
+----+-----
+ 10 | ten
+(1 row)
+
+insert into parted_conflict_test_1 values ('10', 'ten three')
+  on conflict (a) do update set b = excluded.b;
+select * from parted_conflict_test;
+ a  |     b     
+----+-----------
+ 10 | ten three
+(1 row)
+
+drop table parted_conflict_test;
+-- a partitioned table with an index and no corresponding index on the
+-- partition; should raise an error
+create table parted_conflict_test (a int, b text) partition by range (a);
+create table parted_conflict_test_1 partition of parted_conflict_test for values from (0) to (10000);
+alter table only parted_conflict_test add primary key (a);
+insert into parted_conflict_test values (100, 'hundred');
+insert into parted_conflict_test values (100, 'hundred (two)') on conflict (a) do update set b = excluded.b;
+ERROR:  there is no unique or exclusion constraint matching the ON CONFLICT specification
+select * from parted_conflict_test;
+  a  |    b    
+-----+---------
+ 100 | hundred
+(1 row)
+
 drop table parted_conflict_test;
diff --git a/src/test/regress/sql/insert_conflict.sql b/src/test/regress/sql/insert_conflict.sql
index 32c647e3f8..61758d2ea9 100644
--- a/src/test/regress/sql/insert_conflict.sql
+++ b/src/test/regress/sql/insert_conflict.sql
@@ -472,15 +472,78 @@ select * from selfconflict;
 
 drop table selfconflict;
 
--- check that the following works:
+--
+-- INSERT ON CONFLICT and partitioned tables
+--
+
+-- DO NOTHING works
 -- insert into partitioned_table on conflict do nothing
 create table parted_conflict_test (a int, b char) partition by list (a);
 create table parted_conflict_test_1 partition of parted_conflict_test (b unique) for values in (1);
 insert into parted_conflict_test values (1, 'a') on conflict do nothing;
 insert into parted_conflict_test values (1, 'a') on conflict do nothing;
--- however, on conflict do update is not supported yet
-insert into parted_conflict_test values (1) on conflict (b) do update set a = excluded.a;
--- but it works OK if we target the partition directly
-insert into parted_conflict_test_1 values (1) on conflict (b) do
-update set a = excluded.a;
+drop table parted_conflict_test;
+
+-- simple DO UPDATE works, as long as the tuple remains in the same partition
+create table parted_conflict_test (a int primary key, b text) partition by list (a);
+create table parted_conflict_test_1 partition of parted_conflict_test for values in (1, 2);
+create table parted_conflict_test_2 partition of parted_conflict_test for values in (3, 4);
+insert into parted_conflict_test values (1, 'first');
+insert into parted_conflict_test values (1, 'second')
+  on conflict (a) do nothing;
+insert into parted_conflict_test values (1, 'third')
+  on conflict (a) do update set b = format('%s (was %s)', excluded.b, parted_conflict_test.b);
+select * from parted_conflict_test;
+insert into parted_conflict_test values (1, 'b')
+  on conflict (a) do update set b = 'fourth'
+  where parted_conflict_test.b = 'third (was first)';
+select * from parted_conflict_test;
+insert into parted_conflict_test values (1, 'c')
+  on conflict (a) do update set b = 'fourth'
+  where parted_conflict_test.b = 'b';
+select * from parted_conflict_test;
+insert into parted_conflict_test values (1, 'fifth')
+  on conflict (a) do update set a = parted_conflict_test.a * 2,
+  b = format('%s (was %s)', excluded.b, parted_conflict_test.b);
+select * from parted_conflict_test;
+
+-- targetting the partition directly also works
+insert into parted_conflict_test_1 values (2, 'sixth') on conflict (a) do
+  update set b = format('%s (was %s)', excluded.b, parted_conflict_test_1.b);
+select * from parted_conflict_test;
+drop table parted_conflict_test;
+
+-- moving tuple to another partition in the UPDATE clause is not supported
+create table parted_conflict_test (a int, b text) partition by list (a);
+create table parted_conflict_test_1 partition of parted_conflict_test for values in (1);
+create table parted_conflict_test_2 partition of parted_conflict_test for values in (2);
+insert into parted_conflict_test values (1, 'one');
+insert into parted_conflict_test values (1, 'one two')
+  on conflict (a) do update set a = excluded.a * 2;
+drop table parted_conflict_test;
+
+-- multiple-layered partitioning
+create table parted_conflict_test (a int primary key, b text) partition by range (a);
+create table parted_conflict_test_1 partition of parted_conflict_test
+  for values from (0) to (10000) partition by range (a);
+create table parted_conflict_test_1_1 partition of parted_conflict_test_1
+  for values from (0) to (100);
+insert into parted_conflict_test values ('10', 'ten');
+insert into parted_conflict_test values ('10', 'ten two')
+  on conflict (a) do update set b = excluded.b;
+select * from parted_conflict_test;
+
+insert into parted_conflict_test_1 values ('10', 'ten three')
+  on conflict (a) do update set b = excluded.b;
+select * from parted_conflict_test;
+drop table parted_conflict_test;
+
+-- a partitioned table with an index and no corresponding index on the
+-- partition; should raise an error
+create table parted_conflict_test (a int, b text) partition by range (a);
+create table parted_conflict_test_1 partition of parted_conflict_test for values from (0) to (10000);
+alter table only parted_conflict_test add primary key (a);
+insert into parted_conflict_test values (100, 'hundred');
+insert into parted_conflict_test values (100, 'hundred (two)') on conflict (a) do update set b = excluded.b;
+select * from parted_conflict_test;
 drop table parted_conflict_test;
-- 
2.11.0


--fiaenezarrzylaqp--




^ permalink  raw  reply  [nested|flat] 2+ messages in thread

* [PATCH v47 2/9] give options bitmask to table_delete/table_update
@ 2026-03-30 11:27  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 2+ messages in thread

From: Álvaro Herrera @ 2026-03-30 11:27 UTC (permalink / raw)

---
 src/backend/access/heap/heapam.c         | 16 ++++++++++------
 src/backend/access/heap/heapam_handler.c | 13 ++++++++-----
 src/backend/access/table/tableam.c       |  6 +++---
 src/backend/executor/nodeModifyTable.c   |  9 +++++++--
 src/include/access/heapam.h              |  7 ++++---
 src/include/access/tableam.h             | 23 +++++++++++++++--------
 6 files changed, 47 insertions(+), 27 deletions(-)

diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index d34136d2e94..0645b2d5f58 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2862,8 +2862,8 @@ xmax_infomask_changed(uint16 new_infomask, uint16 old_infomask)
  */
 TM_Result
 heap_delete(Relation relation, const ItemPointerData *tid,
-			CommandId cid, Snapshot crosscheck, bool wait,
-			TM_FailureData *tmfd, bool changingPart)
+			CommandId cid, uint32 options, Snapshot crosscheck,
+			bool wait, TM_FailureData *tmfd)
 {
 	TM_Result	result;
 	TransactionId xid = GetCurrentTransactionId();
@@ -2876,6 +2876,7 @@ heap_delete(Relation relation, const ItemPointerData *tid,
 	TransactionId new_xmax;
 	uint16		new_infomask,
 				new_infomask2;
+	bool		changingPart = (options & TABLE_DELETE_CHANGING_PARTITION) != 0;
 	bool		have_tuple_lock = false;
 	bool		iscombo;
 	bool		all_visible_cleared = false;
@@ -3290,9 +3291,11 @@ simple_heap_delete(Relation relation, const ItemPointerData *tid)
 	TM_FailureData tmfd;
 
 	result = heap_delete(relation, tid,
-						 GetCurrentCommandId(true), InvalidSnapshot,
+						 GetCurrentCommandId(true),
+						 0,
+						 InvalidSnapshot,
 						 true /* wait for commit */ ,
-						 &tmfd, false /* changingPart */ );
+						 &tmfd);
 	switch (result)
 	{
 		case TM_SelfModified:
@@ -3331,7 +3334,7 @@ simple_heap_delete(Relation relation, const ItemPointerData *tid)
  */
 TM_Result
 heap_update(Relation relation, const ItemPointerData *otid, HeapTuple newtup,
-			CommandId cid, Snapshot crosscheck, bool wait,
+			CommandId cid, uint32 options, Snapshot crosscheck, bool wait,
 			TM_FailureData *tmfd, LockTupleMode *lockmode,
 			TU_UpdateIndexes *update_indexes)
 {
@@ -4585,7 +4588,8 @@ simple_heap_update(Relation relation, const ItemPointerData *otid, HeapTuple tup
 	LockTupleMode lockmode;
 
 	result = heap_update(relation, otid, tup,
-						 GetCurrentCommandId(true), InvalidSnapshot,
+						 GetCurrentCommandId(true), 0,
+						 InvalidSnapshot,
 						 true /* wait for commit */ ,
 						 &tmfd, &lockmode, update_indexes);
 	switch (result)
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index cdd153c6b6d..69debeff516 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -313,21 +313,23 @@ heapam_tuple_complete_speculative(Relation relation, TupleTableSlot *slot,
 
 static TM_Result
 heapam_tuple_delete(Relation relation, ItemPointer tid, CommandId cid,
-					Snapshot snapshot, Snapshot crosscheck, bool wait,
-					TM_FailureData *tmfd, bool changingPart)
+					uint32 options, Snapshot snapshot, Snapshot crosscheck,
+					bool wait, TM_FailureData *tmfd)
 {
 	/*
 	 * Currently Deleting of index tuples are handled at vacuum, in case if
 	 * the storage itself is cleaning the dead tuples by itself, it is the
 	 * time to call the index tuple deletion also.
 	 */
-	return heap_delete(relation, tid, cid, crosscheck, wait, tmfd, changingPart);
+	return heap_delete(relation, tid, cid, options, crosscheck, wait,
+					   tmfd);
 }
 
 
 static TM_Result
 heapam_tuple_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
-					CommandId cid, Snapshot snapshot, Snapshot crosscheck,
+					CommandId cid, uint32 options pg_attribute_unused(),
+					Snapshot snapshot, Snapshot crosscheck,
 					bool wait, TM_FailureData *tmfd,
 					LockTupleMode *lockmode, TU_UpdateIndexes *update_indexes)
 {
@@ -339,7 +341,8 @@ heapam_tuple_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,
 	slot->tts_tableOid = RelationGetRelid(relation);
 	tuple->t_tableOid = slot->tts_tableOid;
 
-	result = heap_update(relation, otid, tuple, cid, crosscheck, wait,
+	result = heap_update(relation, otid, tuple, cid, options,
+						 crosscheck, wait,
 						 tmfd, lockmode, update_indexes);
 	ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
 
diff --git a/src/backend/access/table/tableam.c b/src/backend/access/table/tableam.c
index 86481d7c029..68ff0966f1c 100644
--- a/src/backend/access/table/tableam.c
+++ b/src/backend/access/table/tableam.c
@@ -320,9 +320,9 @@ simple_table_tuple_delete(Relation rel, ItemPointer tid, Snapshot snapshot)
 
 	result = table_tuple_delete(rel, tid,
 								GetCurrentCommandId(true),
-								snapshot, InvalidSnapshot,
+								0, snapshot, InvalidSnapshot,
 								true /* wait for commit */ ,
-								&tmfd, false /* changingPart */ );
+								&tmfd);
 
 	switch (result)
 	{
@@ -369,7 +369,7 @@ simple_table_tuple_update(Relation rel, ItemPointer otid,
 
 	result = table_tuple_update(rel, otid, slot,
 								GetCurrentCommandId(true),
-								snapshot, InvalidSnapshot,
+								0, snapshot, InvalidSnapshot,
 								true /* wait for commit */ ,
 								&tmfd, &lockmode, update_indexes);
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 582bcc367c0..76728f08734 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1522,14 +1522,18 @@ ExecDeleteAct(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
 			  ItemPointer tupleid, bool changingPart)
 {
 	EState	   *estate = context->estate;
+	uint32		options = 0;
+
+	if (changingPart)
+		options |= TABLE_DELETE_CHANGING_PARTITION;
 
 	return table_tuple_delete(resultRelInfo->ri_RelationDesc, tupleid,
 							  estate->es_output_cid,
+							  options,
 							  estate->es_snapshot,
 							  estate->es_crosscheck_snapshot,
 							  true /* wait for commit */ ,
-							  &context->tmfd,
-							  changingPart);
+							  &context->tmfd);
 }
 
 /*
@@ -2331,6 +2335,7 @@ lreplace:
 	 */
 	result = table_tuple_update(resultRelationDesc, tupleid, slot,
 								estate->es_output_cid,
+								0,
 								estate->es_snapshot,
 								estate->es_crosscheck_snapshot,
 								true /* wait for commit */ ,
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 6018dacf0f7..77560b22877 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -382,13 +382,14 @@ extern void heap_multi_insert(Relation relation, TupleTableSlot **slots,
 							  int ntuples, CommandId cid, uint32 options,
 							  BulkInsertState bistate);
 extern TM_Result heap_delete(Relation relation, const ItemPointerData *tid,
-							 CommandId cid, Snapshot crosscheck, bool wait,
-							 TM_FailureData *tmfd, bool changingPart);
+							 CommandId cid, uint32 options, Snapshot crosscheck,
+							 bool wait, TM_FailureData *tmfd);
 extern void heap_finish_speculative(Relation relation, const ItemPointerData *tid);
 extern void heap_abort_speculative(Relation relation, const ItemPointerData *tid);
 extern TM_Result heap_update(Relation relation, const ItemPointerData *otid,
 							 HeapTuple newtup,
-							 CommandId cid, Snapshot crosscheck, bool wait,
+							 CommandId cid, uint32 options,
+							 Snapshot crosscheck, bool wait,
 							 TM_FailureData *tmfd, LockTupleMode *lockmode,
 							 TU_UpdateIndexes *update_indexes);
 extern TM_Result heap_lock_tuple(Relation relation, HeapTuple tuple,
diff --git a/src/include/access/tableam.h b/src/include/access/tableam.h
index ab2e7fc1dfe..6cdd949f58d 100644
--- a/src/include/access/tableam.h
+++ b/src/include/access/tableam.h
@@ -287,6 +287,11 @@ typedef struct TM_IndexDeleteOp
 /* Follow update chain and lock latest version of tuple */
 #define TUPLE_LOCK_FLAG_FIND_LAST_VERSION		(1 << 1)
 
+/* "options" flag bits for table_tuple_delete */
+#define TABLE_DELETE_CHANGING_PARTITION			(1 << 0)
+
+/* "options" flag bits for table_tuple_update */
+/* XXX none at present */
 
 /* Typedef for callback function for table_index_build_scan */
 typedef void (*IndexBuildCallback) (Relation index,
@@ -558,17 +563,18 @@ typedef struct TableAmRoutine
 	TM_Result	(*tuple_delete) (Relation rel,
 								 ItemPointer tid,
 								 CommandId cid,
+								 uint32 options,
 								 Snapshot snapshot,
 								 Snapshot crosscheck,
 								 bool wait,
-								 TM_FailureData *tmfd,
-								 bool changingPart);
+								 TM_FailureData *tmfd);
 
 	/* see table_tuple_update() for reference about parameters */
 	TM_Result	(*tuple_update) (Relation rel,
 								 ItemPointer otid,
 								 TupleTableSlot *slot,
 								 CommandId cid,
+								 uint32 options,
 								 Snapshot snapshot,
 								 Snapshot crosscheck,
 								 bool wait,
@@ -1533,12 +1539,12 @@ table_multi_insert(Relation rel, TupleTableSlot **slots, int nslots,
  */
 static inline TM_Result
 table_tuple_delete(Relation rel, ItemPointer tid, CommandId cid,
-				   Snapshot snapshot, Snapshot crosscheck, bool wait,
-				   TM_FailureData *tmfd, bool changingPart)
+				   uint32 options, Snapshot snapshot, Snapshot crosscheck,
+				   bool wait, TM_FailureData *tmfd)
 {
-	return rel->rd_tableam->tuple_delete(rel, tid, cid,
+	return rel->rd_tableam->tuple_delete(rel, tid, cid, options,
 										 snapshot, crosscheck,
-										 wait, tmfd, changingPart);
+										 wait, tmfd);
 }
 
 /*
@@ -1578,12 +1584,13 @@ table_tuple_delete(Relation rel, ItemPointer tid, CommandId cid,
  */
 static inline TM_Result
 table_tuple_update(Relation rel, ItemPointer otid, TupleTableSlot *slot,
-				   CommandId cid, Snapshot snapshot, Snapshot crosscheck,
+				   CommandId cid, uint32 options,
+				   Snapshot snapshot, Snapshot crosscheck,
 				   bool wait, TM_FailureData *tmfd, LockTupleMode *lockmode,
 				   TU_UpdateIndexes *update_indexes)
 {
 	return rel->rd_tableam->tuple_update(rel, otid, slot,
-										 cid, snapshot, crosscheck,
+										 cid, options, snapshot, crosscheck,
 										 wait, tmfd,
 										 lockmode, update_indexes);
 }
-- 
2.47.3


--2fxmamo6mu2qbxgv
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v47-0003-Add-CONCURRENTLY-option-to-REPACK-command.patch"



^ permalink  raw  reply  [nested|flat] 2+ messages in thread


end of thread, other threads:[~2026-03-30 11:27 UTC | newest]

Thread overview: 2+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2018-02-27 23:52 [PATCH v1] fix ON CONFLICT DO UPDATE for partitioned tables Alvaro Herrera <alvherre@alvh.no-ip.org>
2026-03-30 11:27 [PATCH v47 2/9] give options bitmask to table_delete/table_update Álvaro Herrera <alvherre@kurilemu.de>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox