agora inbox for pgsql-hackers@postgresql.org  
help / color / mirror / Atom feed
[PATCH v50 5/8] support repacking tables with exclusion constraints
353+ messages / 1 participants
[nested] [flat]

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



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

* [PATCH 1/2] REPACK: do not require the user to have REPLICATION
@ 2026-04-20 09:38  Álvaro Herrera <alvherre@kurilemu.de>
  0 siblings, 0 replies; 353+ messages in thread

From: Álvaro Herrera @ 2026-04-20 09:38 UTC (permalink / raw)

Although REPACK (CONCURRENTLY) uses replication slots, there is no
concern that the slot will leak data of other users, because the
MAINTAIN privilege on the table is required anyway; requiring
REPLICATION is user-unfriendly without providing any actual protection.

A related aspect is that the REPLICATION attribute is not needed to
prevent REPACK from stealing slots from logical replication, since
commit e76d8c749c31 made REPACK use a separate pool of replication
slots.

Because there are now successful concurrent repack runs in the
regression tests, we're forced to run test_plan_advice under
wal_level=replica.

Author: Antonin Houska <ah@cybertec.at>
Reported-by: Justin Pryzby <pryzby@telsasoft.com>
Reviewed-by: Chao Li <lic@highgo.com>
Discussion: https://postgr.es/m/aeJHPNmL4vVy3oPw@pryzbyj2023
---
 src/backend/commands/repack_worker.c          |  1 -
 .../test_plan_advice/t/001_replan_regress.pl  |  1 +
 src/test/regress/expected/cluster.out         | 20 +++++++++++++++++--
 src/test/regress/sql/cluster.sql              | 11 ++++++++--
 4 files changed, 28 insertions(+), 5 deletions(-)

diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c
index b17edd771e2..e4a4860805b 100644
--- a/src/backend/commands/repack_worker.c
+++ b/src/backend/commands/repack_worker.c
@@ -214,7 +214,6 @@ repack_setup_logical_decoding(Oid relid)
 	/*
 	 * Make sure we can use logical decoding.
 	 */
-	CheckSlotPermissions();
 	CheckLogicalDecodingRequirements(true);
 
 	/*
diff --git a/src/test/modules/test_plan_advice/t/001_replan_regress.pl b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
index 38ffa4d11ae..452b179a665 100644
--- a/src/test/modules/test_plan_advice/t/001_replan_regress.pl
+++ b/src/test/modules/test_plan_advice/t/001_replan_regress.pl
@@ -18,6 +18,7 @@ $node->init();
 # Set up our desired configuration.
 $node->append_conf('postgresql.conf', <<EOM);
 shared_preload_libraries='test_plan_advice'
+wal_level=replica
 pg_plan_advice.always_explain_supplied_advice=false
 pg_plan_advice.feedback_warnings=true
 EOM
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 6127b215a86..e17bc91fae1 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -543,15 +543,17 @@ ERROR:  REPACK (CONCURRENTLY) is not supported for partitioned tables
 HINT:  Consider running the command on individual partitions.
 DROP TABLE clstrpart;
 -- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
 CREATE INDEX ptnowner_i_idx ON ptnowner(i);
 CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
 CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
 ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
 SET SESSION AUTHORIZATION regress_ptnowner;
 CLUSTER ptnowner USING ptnowner_i_idx;
 ERROR:  permission denied for table ptnowner
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
 RESET SESSION AUTHORIZATION;
 ALTER TABLE ptnowner OWNER TO regress_ptnowner;
 CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -560,6 +562,11 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
 SET SESSION AUTHORIZATION regress_ptnowner;
 CLUSTER ptnowner USING ptnowner_i_idx;
 WARNING:  permission denied to execute CLUSTER on "ptnowner2", skipping it
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
+ERROR:  cannot process relation "ptnowner1"
+HINT:  Relation "ptnowner1" has no identity index.
 RESET SESSION AUTHORIZATION;
 SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
   JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
@@ -570,6 +577,15 @@ SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
  ptnowner2 | t
 (3 rows)
 
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+  JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
+  relname  | ?column? 
+-----------+----------
+ ptnowner  | t
+ ptnowner1 | f
+ ptnowner2 | t
+(3 rows)
+
 DROP TABLE ptnowner;
 DROP ROLE regress_ptnowner;
 -- Test CLUSTER with external tuplesorting
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index d14063a9683..1f471a8821a 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -254,14 +254,16 @@ REPACK (CONCURRENTLY) clstrpart;
 DROP TABLE clstrpart;
 
 -- Ownership of partitions is checked
-CREATE TABLE ptnowner(i int unique) PARTITION BY LIST (i);
+CREATE TABLE ptnowner(i int unique not null) PARTITION BY LIST (i);
 CREATE INDEX ptnowner_i_idx ON ptnowner(i);
 CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
-CREATE ROLE regress_ptnowner;
+CREATE ROLE regress_ptnowner LOGIN;
 CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
 ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
 SET SESSION AUTHORIZATION regress_ptnowner;
 CLUSTER ptnowner USING ptnowner_i_idx;
+ALTER TABLE ptnowner1 REPLICA IDENTITY USING INDEX ptnowner1_i_key;
+REPACK (CONCURRENTLY) ptnowner1;
 RESET SESSION AUTHORIZATION;
 ALTER TABLE ptnowner OWNER TO regress_ptnowner;
 CREATE TEMP TABLE ptnowner_oldnodes AS
@@ -269,7 +271,12 @@ CREATE TEMP TABLE ptnowner_oldnodes AS
   JOIN pg_class AS c ON c.oid=tree.relid;
 SET SESSION AUTHORIZATION regress_ptnowner;
 CLUSTER ptnowner USING ptnowner_i_idx;
+-- still can't repack without a replica identity
+ALTER TABLE ptnowner1 REPLICA IDENTITY DEFAULT;
+REPACK (CONCURRENTLY) ptnowner1;
 RESET SESSION AUTHORIZATION;
+SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
+  JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
 SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
   JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";
 DROP TABLE ptnowner;
-- 
2.47.3


--m6zy3l65ushq557m
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="0002-REPACK-do-not-require-LOGIN-privileges.patch"



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


end of thread, other threads:[~2026-04-20 09:38 UTC | newest]

Thread overview: 353+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <alvherre@kurilemu.de>
2026-04-20 09:38 [PATCH 1/2] REPACK: do not require the user to have REPLICATION Á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