agora inbox for [email protected]  
help / color / mirror / Atom feed
Extension ownership and misuse of SET ROLE/SET SESSION AUTHORIZATION
962+ messages / 2 participants
[nested] [flat]

* Extension ownership and misuse of SET ROLE/SET SESSION AUTHORIZATION
@ 2020-02-13 22:55  Tom Lane <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Tom Lane @ 2020-02-13 22:55 UTC (permalink / raw)
  To: [email protected]

pg_dump/restore fail to restore the ownership of an extension correctly:
in practice it'll always end up owned by whoever runs the restore
script.  We've sort of averted our eyes from that up to now, because
it's not a big deal in a world where most extensions have to be
superuser-owned anyway.  But I think it's no longer acceptable in a
world with trusted extensions.  So I started looking into fixing that.

Meanwhile ...

pg_dump and pg_restore have a --role switch, which causes them
to attempt to SET ROLE to the specified user name at startup.

They also have a --use-set-session-authorization switch, which causes
them to use SET SESSION AUTHORIZATION before CREATE, rather than
ALTER OWNER after CREATE, to set the ownership of restored objects.
Obviously, those commands will be issued per-object.

Now, for pg_dump there's no real conflict because --role determines
what we send to the source database server, not what is put into the
dump output.  But AFAICS, these two switches do not work together
in pg_restore.  We'll send SET ROLE at the start of the restore but
it'll be immediately and permanently overridden by the first
SET SESSION AUTHORIZATION.  Moreover, because SetSessionAuthorization
inspects the original (authenticated) user ID to decide if the command
is allowed, the SET ROLE doesn't help pass that permission check
even the first time.

Given the current behavior of SET ROLE and SET SESSION AUTHORIZATION,
I don't actually see any way that we could get these features to
play together.  SET SESSION AUTHORIZATION insists on the originally
authenticated user being a superuser, so that the documented point of
--role (to allow you to start the restore from a not-superuser role)
isn't going to work.  I thought about starting to use SET ROLE for
both purposes, but it checks whether you have role privilege based
on the session userid, so that a previous SET ROLE doesn't get you
past that check even if it was a successful SET ROLE to a superuser.

The quick-and-dirty answer is to disallow these switches from being
used together in pg_restore, and I'm inclined to think maybe we should
do that in the back branches.

But ... the reason I noticed this is that I don't see any way to
restore extension ownership correctly unless we use the SET SESSION
AUTHORIZATION technique.  We don't have ALTER EXTENSION OWNER, and I'm
afraid that we never can have it now that we've institutionalized the
expectation that not all objects within an extension need have the
same owner --- that means ALTER EXTENSION OWNER could not know which
contained objects to change the owner of.  So while it might be an
acceptable restriction that --role prevents use of
--use-set-session-authorization, it's surely not acceptable that
--role is unable to restore extensions correctly.

The outline of a fix that I'm considering is

(1) In the backend, allow SET ROLE to succeed if either the session
userid or the current userid is a member of the desired role.  This
would mean that, given the use-case for --role that you are logging
into an account that can "SET ROLE postgres", it'd work to do

	SET ROLE postgres;
	SET ROLE anybody;
	... create an object to be owned by anybody
	SET ROLE postgres;
	SET ROLE somebodyelse;
	... create an object to be owned by somebodyelse
	SET ROLE postgres;
	... lather rinse repeat

(2) Adjust pg_dump/pg_restore so that instead of SET SESSION
AUTHORIZATION, they use SET ROLE pairs as shown above to control
object ownership, when not using ALTER OWNER.  I'm not sure whether
to rename the --use-set-session-authorization switch ... it'd be
misleadingly named now, but there's backwards compatibility issues
if we change it.  Or maybe keep it and invent a separate
--use-set-role switch, though that opens the door for lots of
confusion.

(3) Adjust pg_dump/pg_restore so that extension ownership is
always restored using SET ROLE, whether you gave that switch or not.

Having said that ... I can't find the discussion right now, but
I recall Peter or Stephen complaining recently about how SET ROLE
and SET SESSION AUTHORIZATION allow more than the SQL spec says
they should.  Do we want to make successful restores dependent
on an even-looser definition of SET ROLE?  If not, how might we
handle this problem without assuming non-SQL semantics?

Thoughts?

			regards, tom lane





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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

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

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

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


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



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

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

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread

* [PATCH v50 5/8] support repacking tables with exclusion constraints
@ 2026-04-02 18:59  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 962+ messages in thread

From: Álvaro Herrera @ 2026-04-02 18:59 UTC (permalink / raw)

---
 src/backend/commands/repack.c | 78 +++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 73eadd1ff4a..03829892d57 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -49,6 +49,7 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_control.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
@@ -199,6 +200,8 @@ static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHea
 											   TransactionId frozenXid,
 											   MultiXactId cutoffMulti);
 static List *build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes);
+static void copy_index_constraints(Relation old_index, Oid new_index_id,
+								   Oid new_heap_id);
 static Relation process_single_relation(RepackStmt *stmt,
 										LOCKMODE lockmode,
 										bool isTopLevel,
@@ -3211,6 +3214,7 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 									 false);
 		newindex = index_create_copy(NewHeap, false, oldindex,
 									 ind->rd_rel->reltablespace, newName);
+		copy_index_constraints(ind, newindex, RelationGetRelid(NewHeap));
 		result = lappend_oid(result, newindex);
 
 		index_close(ind, NoLock);
@@ -3219,6 +3223,80 @@ build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
 	return result;
 }
 
+/*
+ * Create a transient copy of a constraint -- supported by a transient
+ * copy of the index that supports the original constraint.
+ *
+ * When repacking a table that contains exclusion constraints, the executor
+ * relies on these constraints being properly catalogued.  These copies are
+ * to support that.
+ *
+ * We don't need the constraints for anything else (the original constraints
+ * will be there once repack completes), so we add pg_depend entries so that
+ * the are dropped when the transient table is dropped.
+ */
+static void
+copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
+{
+	ScanKeyData skey;
+	Relation	rel;
+	TupleDesc	desc;
+	SysScanDesc scan;
+	HeapTuple	tup;
+	ObjectAddress	objrel;
+
+	rel = table_open(ConstraintRelationId, RowExclusiveLock);
+	ObjectAddressSet(objrel, RelationRelationId, new_heap_id);
+
+	/*
+	 * Retrieve the constraints supported by the old index and create an
+	 * identical one that points to the new index.
+	 */
+	ScanKeyInit(&skey,
+				Anum_pg_constraint_conrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(old_index->rd_index->indrelid));
+	scan = systable_beginscan(rel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &skey);
+	desc = RelationGetDescr(rel);
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup);
+		Oid			oid;
+		Datum		values[Natts_pg_constraint] = {0};
+		bool		nulls[Natts_pg_constraint] = {0};
+		bool		replaces[Natts_pg_constraint] = {0};
+		HeapTuple	new_tup;
+		ObjectAddress objcon;
+
+		if (conform->conindid != RelationGetRelid(old_index))
+			continue;
+
+		oid = GetNewOidWithIndex(rel, ConstraintOidIndexId,
+								 Anum_pg_constraint_oid);
+		values[Anum_pg_constraint_oid - 1] = ObjectIdGetDatum(oid);
+		replaces[Anum_pg_constraint_oid - 1] = true;
+		values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(new_heap_id);
+		replaces[Anum_pg_constraint_conrelid - 1] = true;
+		values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(new_index_id);
+		replaces[Anum_pg_constraint_conindid - 1] = true;
+
+		new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
+
+		/* Insert it into the catalog. */
+		CatalogTupleInsert(rel, new_tup);
+
+		/* Create a dependency so it's removed when we drop the new heap. */
+		ObjectAddressSet(objcon, ConstraintRelationId, oid);
+		recordDependencyOn(&objcon, &objrel, DEPENDENCY_AUTO);
+	}
+	systable_endscan(scan);
+
+	table_close(rel, RowExclusiveLock);
+
+	CommandCounterIncrement();
+}
+
 /*
  * Try to start a background worker to perform logical decoding of data
  * changes applied to relation while REPACK CONCURRENTLY is copying its
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0006-Error-out-any-process-that-would-block-at-REPACK.patch"



^ permalink  raw  reply  [nested|flat] 962+ messages in thread


end of thread, other threads:[~2026-04-02 18:59 UTC | newest]

Thread overview: 962+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2020-02-13 22:55 Extension ownership and misuse of SET ROLE/SET SESSION AUTHORIZATION Tom Lane <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>
2026-04-02 18:59 [PATCH v50 5/8] support repacking tables with exclusion constraints Álvaro Herrera <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox